diff --git a/docs/_sass/_main.scss b/docs/_sass/_main.scss
index eaaa1c63badb..9276443c3813 100644
--- a/docs/_sass/_main.scss
+++ b/docs/_sass/_main.scss
@@ -712,41 +712,11 @@ button {
}
}
-#floating-concierge-button-global {
- position: fixed;
- display: block;
- @include breakpoint($breakpoint-tablet) {
- display: none;
- }
-}
-
-#floating-concierge-button-lhn {
- position: absolute;
- display: none;
- @include breakpoint($breakpoint-tablet) {
- display: block;
- }
-}
-
.get-help {
flex-wrap: wrap;
margin-top: 40px;
}
-.floating-concierge-button {
- bottom: 2rem;
- right: 2rem;
-
- img {
- width: 4rem;
- height: 4rem;
-
- &:hover {
- filter: saturate(2);
- }
- }
-}
-
.disable-scrollbar {
@media screen and (max-width: $breakpoint-tablet) {
overflow: hidden;
diff --git a/docs/articles/expensify-classic/integrations/accounting-integrations/Certinia.md b/docs/articles/expensify-classic/integrations/accounting-integrations/Certinia.md
new file mode 100644
index 000000000000..65361ba1af9a
--- /dev/null
+++ b/docs/articles/expensify-classic/integrations/accounting-integrations/Certinia.md
@@ -0,0 +1,150 @@
+---
+title: Certinia
+description: Guide to connecting Expensify and Certinia FFA and PSA/SRP (formerly known as FinancialForce)
+---
+# Overview
+[Cetinia](https://use.expensify.com/financialforce) (Formerly known as FinancialForce)is a cloud-based software solution that provides a range of financial management and accounting applications built on the Salesforce platform. There are two versions: PSA/SRP and FFA and we support both.
+
+# Before connecting to Certinia
+Install the Expensify bundle in Certinia using the relevant installer:
+* [PSA/SRP](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t2M000002J0BHD%252Fpackaging%252FinstallPackage.apexp%253Fp0%253D04t2M000002J0BH)
+* [FFA](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t4p000001UQVj)
+
+## Check contact details in Certinia
+First, make sure you have a user and contact in Certinia that match your main email in Expensify. Then, create contacts for all employees who will be sending expense reports. Ensure that each contact's email matches the one they use in their Expensify account.
+
+## If you use PSA/SRP
+Each report approver needs both a User and a Contact. The user does not need to have a SalesForce license. These can be free chatter users.
+Set permission controls in Certinia for your user for each contact/resource.
+* Go to Permission Controls
+ - Create a new permission control
+ - Set yourself (exporter) as the user
+ - Select the resource (report submitter)
+ - Grant all available permissions
+* Set permissions on any project you are exporting to
+ - Go to **Projects** > _select a project_ > **Project Attributes** > **Allow Expenses Without Assignment**
+ - Select the project > **Edit**
+ - Under the Project Attributes section, check **Allow Expenses Without Assignment**
+* Set up Expense Types (categories in Expensify - _SRP only_)
+ - Go to **Main Menu** > _+ symbol_ > **Expense Type GLA Mappings**
+ - Click **New** to add new mappings
+
+# How to connect to Certinia
+1. Go to **Settings** > **Workspaces** > **Groups** > _[Workspace Name]_ > **Connections** in Expensify
+2. Click **Create a New Certinia (FinancialForce) Connection**
+3. Log into your Certinia account
+4. Expensify and Certinia will begin to sync (in Expensify)
+
+# How to configure export settings for Certinia
+## Preferred Exporter
+The preferred exporter is the user who will be the main exporter of reports. This person will receive the notifications for errors.
+
+## Payable Invoice Status and Date
+Reports can be exported as Complete or In Progress, using date of last expense, submitted date or exported date.
+
+## Reimbursable and non-reimbursable exports
+Both reimbursable and non-reimbursable reports are exported as payable invoices (FFA) or expense reports (PSA/SRP). If you have both Reimbursable and Non-Reimbursable expenses on a single report, we will create a separate payable invoice/expense report for each type.
+
+## Default Vendor (FFA)
+Choose from the full list of vendors from your Certinia FFA account, this will be applied to the non-reimbursable payable invoices.
+
+# How to Configure coding for Certinia
+## Company
+Select which FinancialForce company to import from/export to.
+
+## Chart of Accounts (FFA)
+Prepaid Expense Type and Profit & Loss accounts are imported to be used as categories on each expense.
+
+## Expense Type GLA Mappings (PSA/SRP)
+Your Expense Type GLA Mappings are enabled in Expensify to use as categories on each expense when using both PSA and SRP; however, PSA will not import or export categories, while SRP will.
+
+## Dimensions (FFA)
+We import four dimension levels and each has three options to select from:
+
+* Do not map: FinancialForce defaults will apply to the payable invoice, without importing into Expensify
+* Tags: These are shown in the Tag section of your workspace, and employees can select them on each expense created
+* Report fields: These will show in the Reports section of your workspace. Employees can select one to be applied at the header level i.e. the entire report.
+
+## Projects, Assignments, or Projects & Assignments (PSA/SRP)
+These can be imported as tags with **Milestones** being optional. When selecting to import only projects, we will derive the account from the project. If an assignment is selected, we will derive both the account and project from the assignment.
+
+Note: If you are using a project that does not have an assignment, the box **Allow Expenses Without Assignment** must be checked on the project in FinancialForce.
+
+## Tax
+Import tax rates from Certinia to apply to expenses.
+
+# How to configure advanced settings for Certinia
+## Auto Sync
+Auto Sync in Certinia performs daily updates to your coding. Additionally, it automatically exports reports after they receive final approval. For Non-Reimbursable expenses, syncing happens immediately upon final approval of the report. In the case of Reimbursable expenses, syncing occurs as soon as the report is reimbursed or marked as reimbursed.
+
+## Export tax as non-billable
+When exporting Billable expenses, this dictates whether you will also bill the tax component to your clients/customers.
+
+# Deep Dive
+## Multi-Currency in Certinia PSA/SRP
+When exporting to Certinia PSA/SRP you may see up to three different currencies on the expense report in Certinia, if employees are submitting expenses in more than one original currency.
+* Summary Total Reimbursement Amount: this currency is derived from the currency of the project selected on the expense.
+* Amount field on the Expense line: this currency is derived from the Expensify workspace default report currency.
+* Reimbursable Amount on the Expense line: this currency is derived from the currency of the resource with an email matching the report submitter.
+
+# FAQ
+## What happens if the report can’t be exported to Certinia?
+* The preferred exporter will receive an email outlining the issue and any specific error messages
+* Any error messages preventing the export from taking place will be recorded in the report’s history
+* The report will be listed in the exporter’s Expensify Inbox as awaiting export.
+
+## If I enable Auto Sync, what happens to existing approved and reimbursed reports?
+You can activate Auto Sync without worry because it relies on Final Approval to trigger auto-export. Existing Approved reports won't be affected. However, for Approved reports that haven't been exported to Certinia, you'll need to either manually export them or mark them as manually entered.
+
+## How do I export tax?
+Tax rates are created in Expensify through the tax tracking feature under **Settings** > **Workspaces** > **Groups** > _[Workspace Name]_ > **Tax**. We export the tax amount calculated on the expenses.
+
+## How do reports map to Payable Invoices in Certinia FFA?
+* Account Name - Account associated with Expensify submitter’s email address
+* Reference 1 - Report URL
+* Invoice Description - Report title
+
+## How do reports map to Expense Reports in Certinia PSA/SRP?
+* Expense report name - Report title
+* Resource - User associated with Expensify submitter’s email address
+* Description - Report URL
+* Approver - Expensify report approver
+
+# Sync and Export Errors
+
+## ExpensiError FF0047: You must have an Ops Edit permission to edit approved records.
+This error indicates that the permission control setup between the connected user and the report submitter or region is missing Ops Edit permission.
+
+In Certinia go to Permission Controls and click the one you need to edit. Make sure that Expense Ops Edit is selected under Permissions.
+
+## ExpensiError FF0076: Could not find employee in Certinia
+Go to Contacts in Certinia and add the report creator/submitter's Expensify email address to their employee record, or create a record with that email listed.
+
+If a record already exists then search for their email address to confirm it is not associated with multiple records.
+
+## ExpensiError FF0089: Expense Reports for this Project require an Assignment
+This error indicates that the project needs to have the permissions adjusted in Certinia
+
+Go to Projects > [project name] > Project Attributes and check Allow Expense Without Assignment.
+
+## ExpensiError FF0091: Bad Field Name — [field] is invalid for [object]
+This means the field in question is not accessible to the user profile in Certinia for the user whose credentials were used to make the connection within Expensify.
+
+To correct this:
+* Go to Setup > Build > expand Create > Object within Certinia
+* Then go to Payable Invoice > Custom Fields and Relationships
+* Click View Field Accessibility
+* Find the employee profile in the list and select Hidden
+* Make sure both checkboxes for Visible are selected
+
+Once this step has been completed, sync the connection within Expensify by going to **Settings** > **Workspaces** > **Groups** > _[Workspace Name]_ > **Connections** > **Sync Now** and then attempt to export the report again.
+
+## ExpensiError FF0132: Insufficient access. Make sure you are connecting to Certinia with a user that has the 'Modify All Data' permission
+
+Log into Certinia and go to Setup > Manage Users > Users and find the user whose credentials made the connection.
+
+* Click on their profile on the far right side of the page
+* Go to System > System Permissions
+* Enable Modify All Data and save
+
+Sync the connection within Expensify by going to **Settings** > **Workspaces** > **Groups** > _[Workspace Name]_ > **Connections** > **Sync Now** and then attempt to export the report again
diff --git a/docs/articles/expensify-classic/integrations/accounting-integrations/FinancalForce.md b/docs/articles/expensify-classic/integrations/accounting-integrations/FinancalForce.md
deleted file mode 100644
index 18c78ac7fc10..000000000000
--- a/docs/articles/expensify-classic/integrations/accounting-integrations/FinancalForce.md
+++ /dev/null
@@ -1,111 +0,0 @@
----
-title: Financial Force
-description: Guide to connecting Expensify and FinancialForce FFA and PSA/SRP
----
-# Overview
-[FinancialForce](https://use.expensify.com/financialforce) is a cloud-based software solution that provides a range of financial management and accounting applications built on the Salesforce platform. There are two versions: PSA/SRP and FFA and we support both.
-
-# Before connecting to FinancialForce
-Install the Expensify bundle in SalesForce using the relevant installer:
-* [PSA/SRP](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t2M000002J0BHD%252Fpackaging%252FinstallPackage.apexp%253Fp0%253D04t2M000002J0BH)
-* [FFA](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t4p000001UQVj)
-
-## Check contact details in FF
-First, make sure you have a user and contact in FinancialForce that match your main email in Expensify. Then, create contacts for all employees who will be sending expense reports. Ensure that each contact's email matches the one they use in their Expensify account.
-
-## If you use PSA/SRP
-Each report approver needs both a User and a Contact. The user does not need to have a SalesForce license. These can be free chatter users.
-Set permission controls in FinancialForce for your user for each contact/resource.
-* Go to Permission Controls
- - Create a new permission control
- - Set yourself (exporter) as the user
- - Select the resource (report submitter)
- - Grant all available permissions
-* Set permissions on any project you are exporting to
- - Go to **Projects** > _select a project_ > **Project Attributes** > **Allow Expenses Without Assignment**
- - Select the project > **Edit**
- - Under the Project Attributes section, check **Allow Expenses Without Assignment**
-* Set up Expense Types (categories in Expensify - _SRP only_)
- - Go to **Main Menu** > _+ symbol_ > **Expense Type GLA Mappings**
- - Click **New** to add new mappings
-
-# How to connect to FinancialForce
-1. Go to **Settings** > **Policies** > **Groups** > _[Policy Name]_ > **Connections in Expensify**
-2. Click **Create a New FinancialForce Connection**
-3. Log into your FinancialForce account
-4. Expensify and FinancialForce will begin to sync (in Expensify)
-
-# How to configure export settings for FinancialForce
-## Preferred Exporter
-The preferred exporter is the user who will be the main exporter of reports. This person will receive the notifications for errors.
-
-## Payable Invoice Status and Date
-Reports can be exported as Complete or In Progress, using date of last expense, submitted date or exported date.
-
-## Reimbursable and non-reimbursable exports
-Both reimbursable and non-reimbursable reports are exported as payable invoices (FFA) or expense reports (PSA/SRP). If you have both Reimbursable and Non-Reimbursable expenses on a single report, we will create a separate payable invoice/expense report for each type.
-
-## Default Vendor (FFA)
-Choose from the full list of vendors from your FinancialForce FFA account, this will be applied to the non-reimbursable payable invoices.
-
-# How to Configure coding for Financial Force
-## Company
-Select which FinancialForce company to import from/export to.
-
-## Chart of Accounts (FFA)
-Prepaid Expense Type and Profit & Loss accounts are imported to be used as categories on each expense.
-
-## Expense Type GLA Mappings (PSA/SRP)
-Your Expense Type GLA Mappings are enabled in Expensify to use as categories on each expense when using both PSA and SRP; however, PSA will not import or export categories, while SRP will.
-
-## Dimensions (FFA)
-We import four dimension levels and each has three options to select from:
-
-* Do not map: FinancialForce defaults will apply to the payable invoice, without importing into Expensify
-* Tags: These are shown in the Tag section of your policy, and employees can select them on each expense created
-* Report fields: These will show in the Reports section of your policy. Employees can select one to be applied at the header level i.e. the entire report.
-
-## Projects, Assignments, or Projects & Assignments (PSA/SRP)
-These can be imported as tags with **Milestones** being optional. When selecting to import only projects, we will derive the account from the project. If an assignment is selected, we will derive both the account and project from the assignment.
-
-Note: If you are using a project that does not have an assignment, the box **Allow Expenses Without Assignment** must be checked on the project in FinancialForce.
-
-## Tax
-Import tax rates from FinancialForce to apply to expenses.
-
-# How to configure advanced settings for Financial Force
-## Auto Sync
-Auto Sync in FinancialForce performs daily updates to your coding. Additionally, it automatically exports reports after they receive final approval. For Non-Reimbursable expenses, syncing happens immediately upon final approval of the report. In the case of Reimbursable expenses, syncing occurs as soon as the report is reimbursed or marked as reimbursed.
-
-## Export tax as non-billable
-When exporting Billable expenses, this dictates whether you will also bill the tax component to your clients/customers.
-
-# Deep Dive
-## Multi-Currency in FinancialForce PSA/SRP
-When exporting to FinancialForce PSA/SRP you may see up to three different currencies on the expense report in FinancialForce, if employees are submitting expenses in more than one original currency.
-* Summary Total Reimbursement Amount: this currency is derived from the currency of the project selected on the expense.
-* Amount field on the Expense line: this currency is derived from the Expensify policy default report currency.
-* Reimbursable Amount on the Expense line: this currency is derived from the currency of the resource with an email matching the report submitter.
-
-# FAQ
-## What happens if the report can’t be exported to FinancialForce?
-* The preferred exporter will receive an email outlining the issue and any specific error messages
-* Any error messages preventing the export from taking place will be recorded in the report’s history
-* The report will be listed in the exporter’s Expensify Inbox as awaiting export.
-
-## If I enable Auto Sync, what happens to existing approved and reimbursed reports?
-You can activate Auto Sync without worry because it relies on Final Approval to trigger auto-export. Existing Approved reports won't be affected. However, for Approved reports that haven't been exported to FinancialForce, you'll need to either manually export them or mark them as manually entered.
-
-## How do I export tax?
-Tax rates are created in Expensify through the tax tracking feature under **Settings** > **Policies** > **Group** > _[Policy Name]_ > **Tax**. We export the tax amount calculated on the expenses.
-
-## How do reports map to Payable Invoices in FinancialForce FFA?
-* Account Name - Account associated with Expensify submitter’s email address
-* Reference 1 - Report URL
-* Invoice Description - Report title
-
-## How do reports map to Expense Reports in FinancialForce PSA/SRP?
-* Expense report name - Report title
-* Resource - User associated with Expensify submitter’s email address
-* Description - Report URL
-* Approver - Expensify report approver
diff --git a/docs/articles/expensify-classic/workspace-and-domain-settings/Budgets.md b/docs/articles/expensify-classic/workspace-and-domain-settings/Budgets.md
new file mode 100644
index 000000000000..3c5bc0fe2421
--- /dev/null
+++ b/docs/articles/expensify-classic/workspace-and-domain-settings/Budgets.md
@@ -0,0 +1,56 @@
+---
+title: Budgets
+description: Track employee spending across categories and tags by using Expensify's Budgets feature.
+---
+
+# About
+Expensify’s Budgets feature allows you to:
+- Set monthly and yearly budgets
+- Track spending across categories and tags on an individual and workspace basis
+- Get notified when a budget has met specific thresholds
+
+# How-to
+## Category Budgets
+1. Navigate to **Settings > Group > [Workspace Name] > Categories**
+2. Click the **Edit Rules** button for the category you want to add a budget to
+3. Select the **Budget** tab at the top of the modal that opens
+4. Click the switch next to **Enable Budget**
+5. Once enabled, you will see additional settings to configure:
+ - **Budget frequency**: you can select if you want this to be a monthly or a yearly budget
+ - **Total workspace budget**: you can enter an amount if you want to set a budget for the entire workspace
+ - **Per individual budget**: you can enter an amount if you want to set a budget per person
+ - **Notification threshold** - this is the % in which you will be notified as the budgets are hit
+
+## Single-level Tag Budgets
+1. Navigate to **Settings > Group > [Workspace Name] > Tags**
+2. Click **Edit Budget** next to the tag you want to add a budget to
+3. Click the switch next to **Enable Budget**
+4. Once enabled, you will see additional settings to configure:
+ - **Budget frequency**: you can select if you want this to be a monthly or a yearly budget
+ - **Total workspace budget**: you can enter an amount if you want to set a budget for the entire workspace
+ - **Per individual budget**: you can enter an amount if you want to set a budget per person
+ - **Notification threshold** - this is the % in which you will be notified as the budgets are hit
+
+## Multi-level Tag Budgets
+1. Navigate to **Settings > Group > [Workspace Name] > Tags**
+2. Click the **Edit Tags** button
+3. Click the **Edit Budget** button next to the subtag you want to apply a budget to
+4. Click the switch next to **Enable Budget**
+5. Once enabled, you will see additional settings to configure:
+ - **Budget frequency**: you can select if you want this to be a monthly or a yearly budget
+ - **Total workspace budget**: you can enter an amount if you want to set a budget for the entire workspace
+ - **Per individual budget**: you can enter an amount if you want to set a budget per person
+ - **Notification threshold** - this is the % in which you will be notified as the budgets are hit
+
+# FAQ
+## Can I import budgets as a CSV?
+At this time, you cannot import budgets via CSV since we don’t import categories or tags from direct accounting integrations.
+
+## When will I be notified as a budget is hit?
+Notifications are sent twice:
+ - When your notification threshold is hit (i.e, if you set this as 50%, you’ll be notified when 50% of the budget is met)
+ - When 100% of the budget is met
+
+## How will I be notified when a budget is hit?
+A message will be sent in the #admins room of the Workspace.
+
diff --git a/docs/articles/new-expensify/get-paid-back/Referral-Program.md b/docs/articles/new-expensify/get-paid-back/Referral-Program.md
index 683e93d0277a..6ffb923aeb76 100644
--- a/docs/articles/new-expensify/get-paid-back/Referral-Program.md
+++ b/docs/articles/new-expensify/get-paid-back/Referral-Program.md
@@ -1,53 +1,56 @@
---
-title: Expensify Referral Program
-description: Send your joining link, submit a receipt or invoice, and we'll pay you if your referral adopts Expensify.
+title: New Expensify Referral Program
+description: Share your invite link with a friend, start a chat with a coworker, request money from your boss -- we'll pay you $250 if your referral adopts New Expensify.
---
# About
-Expensify has grown thanks to our users who love Expensify so much that they tell their friends, colleagues, managers, and fellow business founders to use it, too.
+[New Expensify](https://new.expensify.com/) is growing thanks to members like you who love it so much that they tell their friends, family, colleagues, managers, and fellow business founders to use it, too.
-As a thank you, every time you bring a new user into the platform who directly or indirectly leads to the adoption of a paid annual plan on Expensify, you will earn $250.
+As a thank you, every time you bring a new customer into New Expensify, you'll get $250. Here's how it works.
-# How to get paid for referring people to Expensify
+# How to get paid to refer anyone to New Expensify
-1. Submit a report or invoice, or share your referral link with anyone you know who is spending too much time on expenses, or works at a company that could benefit from using Expensify.
+The sky's the limit for this referral program! Your referral can be anyone - a friend, family member, boss, coworker, neighbor, or even social media follower. We're making it as easy as possible to get that cold hard $$$.
-2. You will get $250 for any referred business that commits to an annual subscription, has 2 or more active users, and makes two monthly payments.
+1. There are a bunch of different ways to refer someone to New Expensify:
+ - Start a chat
+ - Request money
+ - Send money
+ - Split a bill
+ - Assign them a task
+ - @ mention them
+ - Invite them to a room
+ - Add them to a workspace
+
+2. You'll get $250 for each referral as long as:
+ - You're the first to refer them to Expensify
+ - They start an annual subscription with two or more active users
+ - They make two payments toward that annual subscription
-That’s right! You can refer anyone working at any company you know.
-
-If their company goes on to become an Expensify customer with an annual subscription, and you are the earliest recorded referrer of a user on that company’s paid Expensify Policy, you'll get paid a referral reward.
-
-The best way to start is to submit any receipt to your manager (you'll get paid back and set yourself up for $250 if they start a subscription: win-win!)
-
-Referral rewards for the Spring/Summer 2023 campaign will be paid by direct deposit.
+For now, referral rewards will be paid via direct deposit into bank accounts that are connected to Expensify.
# FAQ
-- **How will I know if I am the first person to refer a company to Expensify?**
+- **How will I know if I'm the first person to refer a company to Expensify?**
-Successful referrers are notified after their referral pays for 2 months of an annual subscription. We will check for the earliest recorded referrer of a user on the policy, and if that is you, then we will let you know.
+Successful referrers are notified after their referral pays for two months of an annual Expensify subscription. We'll check for the earliest recorded referrer of a member on the workspace, and if that's you, we'll let you know.
- **How will you pay me if I am successful?**
-In the Spring 2023 campaign, Expensify will be paying successful referrers via direct deposit to the Deposit-Only account you have on file. Referral payouts will happen once a month for the duration of the campaign. If you do not have a Deposit-Only account at the time of your referral payout, your deposit will be processed in the next batch.
+For now, Expensify will pay successful referrers via direct deposit to the Deposit-Only bank account you have on file. Referral payouts will happen once a month. If you don't have a Deposit-Only bank account connected to Expensify at the time of your referral payout, your deposit will be processed in the next batch.
-Learn how to add a Deposit-Only account [here](https://community.expensify.com/discussion/4641/how-to-add-a-deposit-only-bank-account-both-personal-and-business).
+Learn how to add a Deposit-Only bank account [here](https://community.expensify.com/discussion/4641/how-to-add-a-deposit-only-bank-account-both-personal-and-business).
- **I’m outside of the US, how do I get paid?**
-While our referral payouts are in USD, you will be able to get paid via a Wise Borderless account. Learn more [here](https://community.expensify.com/discussion/5940/how-to-get-reimbursed-outside-the-us-with-wise-for-non-us-employees).
+While our referral payouts are in USD, you'll be able to get paid via a Wise Borderless account. Learn more [here](https://community.expensify.com/discussion/5940/how-to-get-reimbursed-outside-the-us-with-wise-for-non-us-employees).
- **My referral wasn’t counted! How can I appeal?**
-Expensify reserves the right to modify the terms of the referral program at any time, and pays out referral bonuses for eligible companies at its own discretion.
-
-Please send a message to concierge@expensify.com with the billing owner of the company you have referred and our team will review the referral and get back to you.
-
-- **Where can I find my referral link?**
+Expensify reserves the right to modify the terms of the referral program at any time, and pays out referral bonuses for eligible members at its own discretion. If you think there's been a mistake, please send a message to concierge@expensify.com with the email of your referral and our team will review your case.
-Expensify members who are opted-in for our newsletters will have received an email containing their unique referral link.
+- **Where can I find my referral link?**
-On the mobile app, go to **Settings** > **Invite a Friend** > **Share Invite Link** to retrieve your referral link.
+In New Expensify, go to **Settings** > **Share code** > **Get $250** to retrieve your invite link.
diff --git a/docs/index.html b/docs/index.html
index 70bd5f31545a..ceea63cb398a 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -12,9 +12,4 @@
{{ site.data.routes.home.title }}
{% include platform-card.html href=platform.platform_href %}
{% endfor %}
-
-
-
- {% include floating-concierge-button.html id="floating-concierge-button-global" %}
-
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index e707e4c590b7..8989d0ef3542 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -19,7 +19,7 @@
CFBundlePackageTypeAPPLCFBundleShortVersionString
- 1.4.11
+ 1.4.13CFBundleSignature????CFBundleURLTypes
@@ -40,7 +40,7 @@
CFBundleVersion
- 1.4.11.23
+ 1.4.13.6ITSAppUsesNonExemptEncryptionLSApplicationQueriesSchemes
@@ -120,8 +120,6 @@
UIInterfaceOrientationPortraitUIInterfaceOrientationPortraitUpsideDown
- UIUserInterfaceStyle
- DarkUIViewControllerBasedStatusBarAppearance
diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist
index f87a232a7454..95659bf6908d 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -15,10 +15,10 @@
CFBundlePackageTypeBNDLCFBundleShortVersionString
- 1.4.11
+ 1.4.13CFBundleSignature????CFBundleVersion
- 1.4.11.23
+ 1.4.13.6
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 390511397b0e..eebd6ad532d4 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -1,25 +1,25 @@
PODS:
- - Airship (16.11.3):
- - Airship/Automation (= 16.11.3)
- - Airship/Basement (= 16.11.3)
- - Airship/Core (= 16.11.3)
- - Airship/ExtendedActions (= 16.11.3)
- - Airship/MessageCenter (= 16.11.3)
- - Airship/Automation (16.11.3):
+ - Airship (16.12.1):
+ - Airship/Automation (= 16.12.1)
+ - Airship/Basement (= 16.12.1)
+ - Airship/Core (= 16.12.1)
+ - Airship/ExtendedActions (= 16.12.1)
+ - Airship/MessageCenter (= 16.12.1)
+ - Airship/Automation (16.12.1):
- Airship/Core
- - Airship/Basement (16.11.3)
- - Airship/Core (16.11.3):
+ - Airship/Basement (16.12.1)
+ - Airship/Core (16.12.1):
- Airship/Basement
- - Airship/ExtendedActions (16.11.3):
+ - Airship/ExtendedActions (16.12.1):
- Airship/Core
- - Airship/MessageCenter (16.11.3):
+ - Airship/MessageCenter (16.12.1):
- Airship/Core
- - Airship/PreferenceCenter (16.11.3):
+ - Airship/PreferenceCenter (16.12.1):
- Airship/Core
- - AirshipFrameworkProxy (2.0.8):
- - Airship (= 16.11.3)
- - Airship/MessageCenter (= 16.11.3)
- - Airship/PreferenceCenter (= 16.11.3)
+ - AirshipFrameworkProxy (2.1.1):
+ - Airship (= 16.12.1)
+ - Airship/MessageCenter (= 16.12.1)
+ - Airship/PreferenceCenter (= 16.12.1)
- AppAuth (1.6.2):
- AppAuth/Core (= 1.6.2)
- AppAuth/ExternalUserAgent (= 1.6.2)
@@ -558,8 +558,8 @@ PODS:
- React-jsinspector (0.72.4)
- React-logger (0.72.4):
- glog
- - react-native-airship (15.2.6):
- - AirshipFrameworkProxy (= 2.0.8)
+ - react-native-airship (15.3.1):
+ - AirshipFrameworkProxy (= 2.1.1)
- React-Core
- react-native-blob-util (0.17.3):
- React-Core
@@ -777,35 +777,10 @@ PODS:
- React-Core
- RNReactNativeHapticFeedback (1.14.0):
- React-Core
- - RNReanimated (3.5.4):
- - DoubleConversion
- - FBLazyVector
- - glog
- - hermes-engine
- - RCT-Folly
- - RCTRequired
- - RCTTypeSafety
- - React-callinvoker
+ - RNReanimated (3.6.1):
+ - RCT-Folly (= 2021.07.22.00)
- React-Core
- - React-Core/DevSupport
- - React-Core/RCTWebSocket
- - React-CoreModules
- - React-cxxreact
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-jsinspector
- - React-RCTActionSheet
- - React-RCTAnimation
- - React-RCTAppDelegate
- - React-RCTBlob
- - React-RCTImage
- - React-RCTLinking
- - React-RCTNetwork
- - React-RCTSettings
- - React-RCTText
- ReactCommon/turbomodule/core
- - Yoga
- RNScreens (3.21.0):
- React-Core
- React-RCTImage
@@ -1160,8 +1135,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/yoga"
SPEC CHECKSUMS:
- Airship: c70eed50e429f97f5adb285423c7291fb7a032ae
- AirshipFrameworkProxy: 7bc4130c668c6c98e2d4c60fe4c9eb61a999be99
+ Airship: 2f4510b497a8200780752a5e0304a9072bfffb6d
+ AirshipFrameworkProxy: ea1b6c665c798637b93c465b5e505be3011f1d9d
AppAuth: 3bb1d1cd9340bd09f5ed189fb00b1cc28e1e8570
boost: 57d2868c099736d80fcd648bf211b4431e51a558
BVLinearGradient: 421743791a59d259aec53f4c58793aad031da2ca
@@ -1224,7 +1199,7 @@ SPEC CHECKSUMS:
React-jsiexecutor: c7f826e40fa9cab5d37cab6130b1af237332b594
React-jsinspector: aaed4cf551c4a1c98092436518c2d267b13a673f
React-logger: da1ebe05ae06eb6db4b162202faeafac4b435e77
- react-native-airship: 5d19f4ba303481cf4101ff9dee9249ef6a8a6b64
+ react-native-airship: 6ded22e4ca54f2f80db80b7b911c2b9b696d9335
react-native-blob-util: 99f4d79189252f597fe0d810c57a3733b1b1dea6
react-native-cameraroll: 8ffb0af7a5e5de225fd667610e2979fc1f0c2151
react-native-config: 7cd105e71d903104e8919261480858940a6b9c0e
@@ -1280,7 +1255,7 @@ SPEC CHECKSUMS:
rnmapbox-maps: 6f638ec002aa6e906a6f766d69cd45f968d98e64
RNPermissions: 9b086c8f05b2e2faa587fdc31f4c5ab4509728aa
RNReactNativeHapticFeedback: 1e3efeca9628ff9876ee7cdd9edec1b336913f8c
- RNReanimated: ab2e96c6d5591c3dfbb38a464f54c8d17fb34a87
+ RNReanimated: fdbaa9c964bbab7fac50c90862b6cc5f041679b9
RNScreens: d037903436160a4b039d32606668350d2a808806
RNSVG: d00c8f91c3cbf6d476451313a18f04d220d4f396
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
diff --git a/package-lock.json b/package-lock.json
index 63179219c300..5206b9bf8618 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "new.expensify",
- "version": "1.4.11-23",
+ "version": "1.4.13-6",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "new.expensify",
- "version": "1.4.11-23",
+ "version": "1.4.13-6",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@@ -35,13 +35,13 @@
"@react-native-google-signin/google-signin": "^10.0.1",
"@react-native-picker/picker": "^2.4.3",
"@react-navigation/material-top-tabs": "^6.6.3",
- "@react-navigation/native": "6.1.6",
+ "@react-navigation/native": "6.1.8",
"@react-navigation/stack": "6.3.16",
"@react-ng/bounds-observer": "^0.2.1",
"@rnmapbox/maps": "^10.0.11",
"@shopify/flash-list": "^1.6.1",
"@types/node": "^18.14.0",
- "@ua/react-native-airship": "^15.2.6",
+ "@ua/react-native-airship": "^15.3.1",
"awesome-phonenumber": "^5.4.0",
"babel-polyfill": "^6.26.0",
"canvas-size": "^1.2.6",
@@ -100,7 +100,7 @@
"react-native-plaid-link-sdk": "10.8.0",
"react-native-qrcode-svg": "^6.2.0",
"react-native-quick-sqlite": "^8.0.0-beta.2",
- "react-native-reanimated": "3.5.4",
+ "react-native-reanimated": "^3.6.1",
"react-native-render-html": "6.3.1",
"react-native-safe-area-context": "4.4.1",
"react-native-screens": "3.21.0",
@@ -8563,16 +8563,16 @@
}
},
"node_modules/@react-navigation/core": {
- "version": "6.4.8",
- "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-6.4.8.tgz",
- "integrity": "sha512-klZ9Mcf/P2j+5cHMoGyIeurEzyBM2Uq9+NoSFrF6sdV5iCWHLFhrCXuhbBiQ5wVLCKf4lavlkd/DDs47PXs9RQ==",
+ "version": "6.4.10",
+ "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-6.4.10.tgz",
+ "integrity": "sha512-oYhqxETRHNHKsipm/BtGL0LI43Hs2VSFoWMbBdHK9OqgQPjTVUitslgLcPpo4zApCcmBWoOLX2qPxhsBda644A==",
"dependencies": {
- "@react-navigation/routers": "^6.1.8",
+ "@react-navigation/routers": "^6.1.9",
"escape-string-regexp": "^4.0.0",
"nanoid": "^3.1.23",
"query-string": "^7.1.3",
"react-is": "^16.13.0",
- "use-latest-callback": "^0.1.5"
+ "use-latest-callback": "^0.1.7"
},
"peerDependencies": {
"react": "*"
@@ -8608,11 +8608,11 @@
}
},
"node_modules/@react-navigation/native": {
- "version": "6.1.6",
- "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-6.1.6.tgz",
- "integrity": "sha512-14PmSy4JR8HHEk04QkxQ0ZLuqtiQfb4BV9kkMXD2/jI4TZ+yc43OnO6fQ2o9wm+Bq8pY3DxyerC2AjNUz+oH7Q==",
+ "version": "6.1.8",
+ "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-6.1.8.tgz",
+ "integrity": "sha512-0alti852nV+8oCVm9H80G6kZvrHoy51+rXBvVCRUs2rNDDozC/xPZs8tyeCJkqdw3cpxZDK8ndXF22uWq28+0Q==",
"dependencies": {
- "@react-navigation/core": "^6.4.8",
+ "@react-navigation/core": "^6.4.9",
"escape-string-regexp": "^4.0.0",
"fast-deep-equal": "^3.1.3",
"nanoid": "^3.1.23"
@@ -8623,9 +8623,9 @@
}
},
"node_modules/@react-navigation/routers": {
- "version": "6.1.8",
- "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-6.1.8.tgz",
- "integrity": "sha512-CEge+ZLhb1HBrSvv4RwOol7EKLW1QoqVIQlE9TN5MpxS/+VoQvP+cLbuz0Op53/iJfYhtXRFd1ZAd3RTRqto9w==",
+ "version": "6.1.9",
+ "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-6.1.9.tgz",
+ "integrity": "sha512-lTM8gSFHSfkJvQkxacGM6VJtBt61ip2XO54aNfswD+KMw6eeZ4oehl7m0me3CR9hnDE4+60iAZR8sAhvCiI3NA==",
"dependencies": {
"nanoid": "^3.1.23"
}
@@ -20437,9 +20437,9 @@
}
},
"node_modules/@ua/react-native-airship": {
- "version": "15.2.6",
- "resolved": "https://registry.npmjs.org/@ua/react-native-airship/-/react-native-airship-15.2.6.tgz",
- "integrity": "sha512-dVlBPPYXD/4SEshv/X7mmt3xF8WfnNqiSNzCyqJSLAZ1aJuPpP9Z5WemCYsa2iv6goRZvtJSE4P79QKlfoTwXw==",
+ "version": "15.3.1",
+ "resolved": "https://registry.npmjs.org/@ua/react-native-airship/-/react-native-airship-15.3.1.tgz",
+ "integrity": "sha512-g5YX4/fpBJ0ml//7ave8HE68uF4QFriCuei0xJwK+ClzbTDWYB6OldvE/wj5dMpgQ95ZFSbr5LU77muYScxXLQ==",
"engines": {
"node": ">= 16.0.0"
},
@@ -44561,9 +44561,9 @@
}
},
"node_modules/react-native-reanimated": {
- "version": "3.5.4",
- "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.5.4.tgz",
- "integrity": "sha512-8we9LLDO1o4Oj9/DICeEJ2K1tjfqkJagqQUglxeUAkol/HcEJ6PGxIrpBcNryLqCDYEcu6FZWld/FzizBIw6bg==",
+ "version": "3.6.1",
+ "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.6.1.tgz",
+ "integrity": "sha512-F4vG9Yf9PKmE3GaWtVGUpzj3SM6YY2cx1yRHCwiMd1uY7W0gU017LfcVUorboJnj0y5QZqEriEK1Usq2Y8YZqg==",
"dependencies": {
"@babel/plugin-transform-object-assign": "^7.16.7",
"@babel/preset-typescript": "^7.16.7",
@@ -50723,9 +50723,9 @@
}
},
"node_modules/use-latest-callback": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.1.6.tgz",
- "integrity": "sha512-VO/P91A/PmKH9bcN9a7O3duSuxe6M14ZoYXgA6a8dab8doWNdhiIHzEkX/jFeTTRBsX0Ubk6nG4q2NIjNsj+bg==",
+ "version": "0.1.9",
+ "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.1.9.tgz",
+ "integrity": "sha512-CL/29uS74AwreI/f2oz2hLTW7ZqVeV5+gxFeGudzQrgkCytrHw33G4KbnQOrRlAEzzAFXi7dDLMC9zhWcVpzmw==",
"peerDependencies": {
"react": ">=16.8"
}
@@ -58884,16 +58884,16 @@
}
},
"@react-navigation/core": {
- "version": "6.4.8",
- "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-6.4.8.tgz",
- "integrity": "sha512-klZ9Mcf/P2j+5cHMoGyIeurEzyBM2Uq9+NoSFrF6sdV5iCWHLFhrCXuhbBiQ5wVLCKf4lavlkd/DDs47PXs9RQ==",
+ "version": "6.4.10",
+ "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-6.4.10.tgz",
+ "integrity": "sha512-oYhqxETRHNHKsipm/BtGL0LI43Hs2VSFoWMbBdHK9OqgQPjTVUitslgLcPpo4zApCcmBWoOLX2qPxhsBda644A==",
"requires": {
- "@react-navigation/routers": "^6.1.8",
+ "@react-navigation/routers": "^6.1.9",
"escape-string-regexp": "^4.0.0",
"nanoid": "^3.1.23",
"query-string": "^7.1.3",
"react-is": "^16.13.0",
- "use-latest-callback": "^0.1.5"
+ "use-latest-callback": "^0.1.7"
}
},
"@react-navigation/devtools": {
@@ -58915,20 +58915,20 @@
}
},
"@react-navigation/native": {
- "version": "6.1.6",
- "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-6.1.6.tgz",
- "integrity": "sha512-14PmSy4JR8HHEk04QkxQ0ZLuqtiQfb4BV9kkMXD2/jI4TZ+yc43OnO6fQ2o9wm+Bq8pY3DxyerC2AjNUz+oH7Q==",
+ "version": "6.1.8",
+ "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-6.1.8.tgz",
+ "integrity": "sha512-0alti852nV+8oCVm9H80G6kZvrHoy51+rXBvVCRUs2rNDDozC/xPZs8tyeCJkqdw3cpxZDK8ndXF22uWq28+0Q==",
"requires": {
- "@react-navigation/core": "^6.4.8",
+ "@react-navigation/core": "^6.4.9",
"escape-string-regexp": "^4.0.0",
"fast-deep-equal": "^3.1.3",
"nanoid": "^3.1.23"
}
},
"@react-navigation/routers": {
- "version": "6.1.8",
- "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-6.1.8.tgz",
- "integrity": "sha512-CEge+ZLhb1HBrSvv4RwOol7EKLW1QoqVIQlE9TN5MpxS/+VoQvP+cLbuz0Op53/iJfYhtXRFd1ZAd3RTRqto9w==",
+ "version": "6.1.9",
+ "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-6.1.9.tgz",
+ "integrity": "sha512-lTM8gSFHSfkJvQkxacGM6VJtBt61ip2XO54aNfswD+KMw6eeZ4oehl7m0me3CR9hnDE4+60iAZR8sAhvCiI3NA==",
"requires": {
"nanoid": "^3.1.23"
}
@@ -67439,9 +67439,9 @@
}
},
"@ua/react-native-airship": {
- "version": "15.2.6",
- "resolved": "https://registry.npmjs.org/@ua/react-native-airship/-/react-native-airship-15.2.6.tgz",
- "integrity": "sha512-dVlBPPYXD/4SEshv/X7mmt3xF8WfnNqiSNzCyqJSLAZ1aJuPpP9Z5WemCYsa2iv6goRZvtJSE4P79QKlfoTwXw==",
+ "version": "15.3.1",
+ "resolved": "https://registry.npmjs.org/@ua/react-native-airship/-/react-native-airship-15.3.1.tgz",
+ "integrity": "sha512-g5YX4/fpBJ0ml//7ave8HE68uF4QFriCuei0xJwK+ClzbTDWYB6OldvE/wj5dMpgQ95ZFSbr5LU77muYScxXLQ==",
"requires": {}
},
"@vercel/ncc": {
@@ -84883,9 +84883,9 @@
"requires": {}
},
"react-native-reanimated": {
- "version": "3.5.4",
- "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.5.4.tgz",
- "integrity": "sha512-8we9LLDO1o4Oj9/DICeEJ2K1tjfqkJagqQUglxeUAkol/HcEJ6PGxIrpBcNryLqCDYEcu6FZWld/FzizBIw6bg==",
+ "version": "3.6.1",
+ "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.6.1.tgz",
+ "integrity": "sha512-F4vG9Yf9PKmE3GaWtVGUpzj3SM6YY2cx1yRHCwiMd1uY7W0gU017LfcVUorboJnj0y5QZqEriEK1Usq2Y8YZqg==",
"requires": {
"@babel/plugin-transform-object-assign": "^7.16.7",
"@babel/preset-typescript": "^7.16.7",
@@ -89240,9 +89240,9 @@
}
},
"use-latest-callback": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.1.6.tgz",
- "integrity": "sha512-VO/P91A/PmKH9bcN9a7O3duSuxe6M14ZoYXgA6a8dab8doWNdhiIHzEkX/jFeTTRBsX0Ubk6nG4q2NIjNsj+bg==",
+ "version": "0.1.9",
+ "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.1.9.tgz",
+ "integrity": "sha512-CL/29uS74AwreI/f2oz2hLTW7ZqVeV5+gxFeGudzQrgkCytrHw33G4KbnQOrRlAEzzAFXi7dDLMC9zhWcVpzmw==",
"requires": {}
},
"use-memo-one": {
diff --git a/package.json b/package.json
index e03beed0bd8e..8432a773fdf7 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "1.4.11-23",
+ "version": "1.4.13-6",
"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.",
@@ -83,13 +83,13 @@
"@react-native-google-signin/google-signin": "^10.0.1",
"@react-native-picker/picker": "^2.4.3",
"@react-navigation/material-top-tabs": "^6.6.3",
- "@react-navigation/native": "6.1.6",
+ "@react-navigation/native": "6.1.8",
"@react-navigation/stack": "6.3.16",
"@react-ng/bounds-observer": "^0.2.1",
"@rnmapbox/maps": "^10.0.11",
"@shopify/flash-list": "^1.6.1",
"@types/node": "^18.14.0",
- "@ua/react-native-airship": "^15.2.6",
+ "@ua/react-native-airship": "^15.3.1",
"awesome-phonenumber": "^5.4.0",
"babel-polyfill": "^6.26.0",
"canvas-size": "^1.2.6",
@@ -148,7 +148,7 @@
"react-native-plaid-link-sdk": "10.8.0",
"react-native-qrcode-svg": "^6.2.0",
"react-native-quick-sqlite": "^8.0.0-beta.2",
- "react-native-reanimated": "3.5.4",
+ "react-native-reanimated": "^3.6.1",
"react-native-render-html": "6.3.1",
"react-native-safe-area-context": "4.4.1",
"react-native-screens": "3.21.0",
diff --git a/patches/@react-navigation+native+6.1.6.patch b/patches/@react-navigation+native+6.1.8.patch
similarity index 90%
rename from patches/@react-navigation+native+6.1.6.patch
rename to patches/@react-navigation+native+6.1.8.patch
index eb933683c850..c461d7e510fe 100644
--- a/patches/@react-navigation+native+6.1.6.patch
+++ b/patches/@react-navigation+native+6.1.8.patch
@@ -133,7 +133,7 @@ index 0000000..16da117
+//# sourceMappingURL=findFocusedRouteKey.js.map
\ No newline at end of file
diff --git a/node_modules/@react-navigation/native/lib/module/useLinking.js b/node_modules/@react-navigation/native/lib/module/useLinking.js
-index 5bf2a88..a4318ef 100644
+index 6f0ac51..a77b608 100644
--- a/node_modules/@react-navigation/native/lib/module/useLinking.js
+++ b/node_modules/@react-navigation/native/lib/module/useLinking.js
@@ -2,6 +2,7 @@ import { findFocusedRoute, getActionFromState as getActionFromStateDefault, getP
@@ -144,37 +144,7 @@ index 5bf2a88..a4318ef 100644
import ServerContext from './ServerContext';
/**
* Find the matching navigation state that changed between 2 navigation states
-@@ -34,32 +35,52 @@ const findMatchingState = (a, b) => {
- /**
- * Run async function in series as it's called.
- */
--const series = cb => {
-- // Whether we're currently handling a callback
-- let handling = false;
-- let queue = [];
-- const callback = async () => {
-- try {
-- if (handling) {
-- // If we're currently handling a previous event, wait before handling this one
-- // Add the callback to the beginning of the queue
-- queue.unshift(callback);
-- return;
-- }
-- handling = true;
-- await cb();
-- } finally {
-- handling = false;
-- if (queue.length) {
-- // If we have queued items, handle the last one
-- const last = queue.pop();
-- last === null || last === void 0 ? void 0 : last();
-- }
-- }
-+const series = (cb) => {
-+ let queue = Promise.resolve();
-+ const callback = () => {
-+ queue = queue.then(cb);
- };
+@@ -42,6 +43,44 @@ export const series = cb => {
return callback;
};
let linkingHandlers = [];
@@ -219,7 +189,7 @@ index 5bf2a88..a4318ef 100644
export default function useLinking(ref, _ref) {
let {
independent,
-@@ -251,6 +272,9 @@ export default function useLinking(ref, _ref) {
+@@ -231,6 +270,9 @@ export default function useLinking(ref, _ref) {
// Otherwise it's likely a change triggered by `popstate`
path !== pendingPath) {
const historyDelta = (focusedState.history ? focusedState.history.length : focusedState.routes.length) - (previousFocusedState.history ? previousFocusedState.history.length : previousFocusedState.routes.length);
@@ -229,7 +199,7 @@ index 5bf2a88..a4318ef 100644
if (historyDelta > 0) {
// If history length is increased, we should pushState
// Note that path might not actually change here, for example, drawer open should pushState
-@@ -262,34 +286,55 @@ export default function useLinking(ref, _ref) {
+@@ -242,34 +284,55 @@ export default function useLinking(ref, _ref) {
// If history length is decreased, i.e. entries were removed, we want to go back
const nextIndex = history.backIndex({
diff --git a/patches/react-native-fast-image+8.6.3.patch b/patches/react-native-fast-image+8.6.3+001+initial.patch
similarity index 100%
rename from patches/react-native-fast-image+8.6.3.patch
rename to patches/react-native-fast-image+8.6.3+001+initial.patch
diff --git a/patches/react-native-fast-image+8.6.3+002+bitmap-downsampling.patch b/patches/react-native-fast-image+8.6.3+002+bitmap-downsampling.patch
new file mode 100644
index 000000000000..a626d5b16b2f
--- /dev/null
+++ b/patches/react-native-fast-image+8.6.3+002+bitmap-downsampling.patch
@@ -0,0 +1,62 @@
+diff --git a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java
+index 1339f5c..9dfec0c 100644
+--- a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java
++++ b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java
+@@ -176,7 +176,8 @@ class FastImageViewWithUrl extends AppCompatImageView {
+ .apply(FastImageViewConverter
+ .getOptions(context, imageSource, mSource)
+ .placeholder(mDefaultSource) // show until loaded
+- .fallback(mDefaultSource)); // null will not be treated as error
++ .fallback(mDefaultSource))
++ .transform(new ResizeTransformation());
+
+ if (key != null)
+ builder.listener(new FastImageRequestListener(key));
+diff --git a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/ResizeTransformation.java b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/ResizeTransformation.java
+new file mode 100644
+index 0000000..1daa227
+--- /dev/null
++++ b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/ResizeTransformation.java
+@@ -0,0 +1,41 @@
++package com.dylanvann.fastimage;
++
++ import android.content.Context;
++ import android.graphics.Bitmap;
++
++ import androidx.annotation.NonNull;
++
++ import com.bumptech.glide.load.Transformation;
++ import com.bumptech.glide.load.engine.Resource;
++ import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
++ import com.bumptech.glide.load.resource.bitmap.BitmapResource;
++
++ import java.security.MessageDigest;
++
++ public class ResizeTransformation implements Transformation {
++
++ private final double MAX_BYTES = 25000000.0;
++
++ @NonNull
++ @Override
++ public Resource transform(@NonNull Context context, @NonNull Resource resource, int outWidth, int outHeight) {
++ Bitmap toTransform = resource.get();
++
++ if (toTransform.getByteCount() > MAX_BYTES) {
++ double scaleFactor = Math.sqrt(MAX_BYTES / (double) toTransform.getByteCount());
++ int newHeight = (int) (outHeight * scaleFactor);
++ int newWidth = (int) (outWidth * scaleFactor);
++
++ BitmapPool pool = GlideApp.get(context).getBitmapPool();
++ Bitmap scaledBitmap = Bitmap.createScaledBitmap(toTransform, newWidth, newHeight, true);
++ return BitmapResource.obtain(scaledBitmap, pool);
++ }
++
++ return resource;
++ }
++
++ @Override
++ public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
++ messageDigest.update(("ResizeTransformation").getBytes());
++ }
++ }
+\ No newline at end of file
diff --git a/src/App.js b/src/App.js
index e273dcce1e47..3553900bbc7f 100644
--- a/src/App.js
+++ b/src/App.js
@@ -8,14 +8,17 @@ import {SafeAreaProvider} from 'react-native-safe-area-context';
import '../wdyr';
import ColorSchemeWrapper from './components/ColorSchemeWrapper';
import ComposeProviders from './components/ComposeProviders';
-import CustomStatusBar from './components/CustomStatusBar';
-import CustomStatusBarContextProvider from './components/CustomStatusBar/CustomStatusBarContextProvider';
+import CustomStatusBarAndBackground from './components/CustomStatusBarAndBackground';
+import CustomStatusBarAndBackgroundContextProvider from './components/CustomStatusBarAndBackground/CustomStatusBarAndBackgroundContextProvider';
import ErrorBoundary from './components/ErrorBoundary';
import HTMLEngineProvider from './components/HTMLEngineProvider';
import {LocaleContextProvider} from './components/LocaleContextProvider';
import OnyxProvider from './components/OnyxProvider';
import PopoverContextProvider from './components/PopoverProvider';
import SafeArea from './components/SafeArea';
+import ThemeIllustrationsProvider from './components/ThemeIllustrationsProvider';
+import ThemeProvider from './components/ThemeProvider';
+import ThemeStylesProvider from './components/ThemeStylesProvider';
import {CurrentReportIDContextProvider} from './components/withCurrentReportID';
import {EnvironmentProvider} from './components/withEnvironment';
import {KeyboardStateProvider} from './components/withKeyboardState';
@@ -26,9 +29,6 @@ import OnyxUpdateManager from './libs/actions/OnyxUpdateManager';
import * as Session from './libs/actions/Session';
import * as Environment from './libs/Environment/Environment';
import {ReportAttachmentsProvider} from './pages/home/report/ReportAttachmentsContext';
-import ThemeIllustrationsProvider from './styles/illustrations/ThemeIllustrationsProvider';
-import ThemeProvider from './styles/themes/ThemeProvider';
-import ThemeStylesProvider from './styles/ThemeStylesProvider';
// For easier debugging and development, when we are in web we expose Onyx to the window, so you can more easily set data into Onyx
if (window && Environment.isDevelopment()) {
@@ -68,10 +68,10 @@ function App() {
ReportAttachmentsProvider,
PickerStateProvider,
EnvironmentProvider,
- CustomStatusBarContextProvider,
+ CustomStatusBarAndBackgroundContextProvider,
]}
>
-
+
diff --git a/src/CONST.ts b/src/CONST.ts
index 072f780b54ae..219807587a25 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -255,6 +255,7 @@ const CONST = {
BETA_COMMENT_LINKING: 'commentLinking',
POLICY_ROOMS: 'policyRooms',
VIOLATIONS: 'violations',
+ REPORT_FIELDS: 'reportFields',
},
BUTTON_STATES: {
DEFAULT: 'default',
@@ -463,6 +464,7 @@ const CONST = {
ONFIDO_TERMS_OF_SERVICE_URL: 'https://onfido.com/terms-of-service/',
// Use Environment.getEnvironmentURL to get the complete URL with port number
DEV_NEW_EXPENSIFY_URL: 'https://dev.new.expensify.com:',
+ EXPENSIFY_INBOX_URL: 'https://www.expensify.com/inbox',
SIGN_IN_FORM_WIDTH: 300,
@@ -532,11 +534,13 @@ const CONST = {
DELETE_TAG: 'POLICYCHANGELOG_DELETE_TAG',
IMPORT_CUSTOM_UNIT_RATES: 'POLICYCHANGELOG_IMPORT_CUSTOM_UNIT_RATES',
IMPORT_TAGS: 'POLICYCHANGELOG_IMPORT_TAGS',
+ INDIVIDUAL_BUDGET_NOTIFICATION: 'POLICYCHANGELOG_INDIVIDUAL_BUDGET_NOTIFICATION',
INVITE_TO_ROOM: 'POLICYCHANGELOG_INVITETOROOM',
REMOVE_FROM_ROOM: 'POLICYCHANGELOG_REMOVEFROMROOM',
SET_AUTOREIMBURSEMENT: 'POLICYCHANGELOG_SET_AUTOREIMBURSEMENT',
SET_AUTO_JOIN: 'POLICYCHANGELOG_SET_AUTO_JOIN',
SET_CATEGORY_NAME: 'POLICYCHANGELOG_SET_CATEGORY_NAME',
+ SHARED_BUDGET_NOTIFICATION: 'POLICYCHANGELOG_SHARED_BUDGET_NOTIFICATION',
UPDATE_ACH_ACCOUNT: 'POLICYCHANGELOG_UPDATE_ACH_ACCOUNT',
UPDATE_APPROVER_RULE: 'POLICYCHANGELOG_UPDATE_APPROVER_RULE',
UPDATE_AUDIT_RATE: 'POLICYCHANGELOG_UPDATE_AUDIT_RATE',
@@ -687,6 +691,7 @@ const CONST = {
TIMING: {
CALCULATE_MOST_RECENT_LAST_MODIFIED_ACTION: 'calc_most_recent_last_modified_action',
SEARCH_RENDER: 'search_render',
+ CHAT_RENDER: 'chat_render',
HOMEPAGE_INITIAL_RENDER: 'homepage_initial_render',
REPORT_INITIAL_RENDER: 'report_initial_render',
SWITCH_REPORT: 'switch_report',
@@ -706,9 +711,10 @@ const CONST = {
DEFAULT: 'default',
},
THEME: {
- DEFAULT: 'dark',
- LIGHT: 'light',
+ DEFAULT: 'system',
+ FALLBACK: 'dark',
DARK: 'dark',
+ LIGHT: 'light',
SYSTEM: 'system',
},
COLOR_SCHEME: {
@@ -2902,6 +2908,10 @@ const CONST = {
NAVIGATE: 'NAVIGATE',
},
},
+ TIME_PERIOD: {
+ AM: 'AM',
+ PM: 'PM',
+ },
INDENTS: ' ',
PARENT_CHILD_SEPARATOR: ': ',
CATEGORY_LIST_THRESHOLD: 8,
@@ -2911,7 +2921,7 @@ const CONST = {
SBE: 'SbeDemoSetup',
MONEY2020: 'Money2020DemoSetup',
},
-
+ COLON: ':',
MAPBOX: {
PADDING: 50,
DEFAULT_ZOOM: 10,
@@ -2957,7 +2967,7 @@ const CONST = {
SHARE_CODE: 'shareCode',
},
REVENUE: 250,
- LEARN_MORE_LINK: 'https://help.expensify.com/articles/new-expensify/billing-and-plan-types/Referral-Program',
+ LEARN_MORE_LINK: 'https://help.expensify.com/articles/new-expensify/get-paid-back/Referral-Program',
LINK: 'https://join.my.expensify.com',
},
diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts
index 933ae678da23..0cc7934ad007 100755
--- a/src/ONYXKEYS.ts
+++ b/src/ONYXKEYS.ts
@@ -334,10 +334,10 @@ const ONYXKEYS = {
WAYPOINT_FORM_DRAFT: 'waypointFormDraft',
SETTINGS_STATUS_SET_FORM: 'settingsStatusSetForm',
SETTINGS_STATUS_SET_FORM_DRAFT: 'settingsStatusSetFormDraft',
- SETTINGS_STATUS_CLEAR_AFTER_FORM: 'settingsStatusClearAfterForm',
- SETTINGS_STATUS_CLEAR_AFTER_FORM_DRAFT: 'settingsStatusClearAfterFormDraft',
SETTINGS_STATUS_SET_CLEAR_AFTER_FORM: 'settingsStatusSetClearAfterForm',
SETTINGS_STATUS_SET_CLEAR_AFTER_FORM_DRAFT: 'settingsStatusSetClearAfterFormDraft',
+ SETTINGS_STATUS_CLEAR_DATE_FORM: 'settingsStatusClearDateForm',
+ SETTINGS_STATUS_CLEAR_DATE_FORM_DRAFT: 'settingsStatusClearDateFormDraft',
PRIVATE_NOTES_FORM: 'privateNotesForm',
PRIVATE_NOTES_FORM_DRAFT: 'privateNotesFormDraft',
I_KNOW_A_TEACHER_FORM: 'iKnowTeacherForm',
@@ -508,8 +508,8 @@ type OnyxValues = {
[ONYXKEYS.FORMS.WAYPOINT_FORM_DRAFT]: OnyxTypes.Form;
[ONYXKEYS.FORMS.SETTINGS_STATUS_SET_FORM]: OnyxTypes.Form;
[ONYXKEYS.FORMS.SETTINGS_STATUS_SET_FORM_DRAFT]: OnyxTypes.Form;
- [ONYXKEYS.FORMS.SETTINGS_STATUS_CLEAR_AFTER_FORM]: OnyxTypes.Form;
- [ONYXKEYS.FORMS.SETTINGS_STATUS_CLEAR_AFTER_FORM_DRAFT]: OnyxTypes.Form;
+ [ONYXKEYS.FORMS.SETTINGS_STATUS_CLEAR_DATE_FORM]: OnyxTypes.Form;
+ [ONYXKEYS.FORMS.SETTINGS_STATUS_CLEAR_DATE_FORM_DRAFT]: OnyxTypes.Form;
[ONYXKEYS.FORMS.SETTINGS_STATUS_SET_CLEAR_AFTER_FORM]: OnyxTypes.Form;
[ONYXKEYS.FORMS.SETTINGS_STATUS_SET_CLEAR_AFTER_FORM_DRAFT]: OnyxTypes.Form;
[ONYXKEYS.FORMS.PRIVATE_NOTES_FORM]: OnyxTypes.Form;
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index 425ff73af56b..ca1fe9f0e81a 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -140,7 +140,9 @@ const ROUTES = {
getRoute: (backTo?: string) => getUrlWithBackToParam('settings/security/two-factor-auth', backTo),
},
SETTINGS_STATUS: 'settings/profile/status',
- SETTINGS_STATUS_SET: 'settings/profile/status/set',
+ SETTINGS_STATUS_CLEAR_AFTER: 'settings/profile/status/clear-after',
+ SETTINGS_STATUS_CLEAR_AFTER_DATE: 'settings/profile/status/clear-after/date',
+ SETTINGS_STATUS_CLEAR_AFTER_TIME: 'settings/profile/status/clear-after/time',
KEYBOARD_SHORTCUTS: 'keyboard-shortcuts',
diff --git a/src/SCREENS.ts b/src/SCREENS.ts
index 921f57953482..2cd263237866 100644
--- a/src/SCREENS.ts
+++ b/src/SCREENS.ts
@@ -37,8 +37,10 @@ const SCREENS = {
CONTACT_METHODS: 'Settings_ContactMethods',
CONTACT_METHOD_DETAILS: 'Settings_ContactMethodDetails',
NEW_CONTACT_METHOD: 'Settings_NewContactMethod',
+ STATUS_CLEAR_AFTER: 'Settings_Status_Clear_After',
+ STATUS_CLEAR_AFTER_DATE: 'Settings_Status_Clear_After_Date',
+ STATUS_CLEAR_AFTER_TIME: 'Settings_Status_Clear_After_Time',
STATUS: 'Settings_Status',
- STATUS_SET: 'Settings_Status_Set',
PRONOUNS: 'Settings_Pronouns',
TIMEZONE: 'Settings_Timezone',
TIMEZONE_SELECT: 'Settings_Timezone_Select',
diff --git a/src/components/AddPaymentMethodMenu.js b/src/components/AddPaymentMethodMenu.js
index 4d01fa108e2a..4abe5655e307 100644
--- a/src/components/AddPaymentMethodMenu.js
+++ b/src/components/AddPaymentMethodMenu.js
@@ -48,6 +48,9 @@ const propTypes = {
/** Currently logged in user accountID */
accountID: PropTypes.number,
}),
+
+ /** Whether the personal bank account option should be shown */
+ shouldShowPersonalBankAccountOption: PropTypes.bool,
};
const defaultProps = {
@@ -59,9 +62,10 @@ const defaultProps = {
},
anchorRef: () => {},
session: {},
+ shouldShowPersonalBankAccountOption: false,
};
-function AddPaymentMethodMenu({isVisible, onClose, anchorPosition, anchorAlignment, anchorRef, iouReport, onItemSelected, session}) {
+function AddPaymentMethodMenu({isVisible, onClose, anchorPosition, anchorAlignment, anchorRef, iouReport, onItemSelected, session, shouldShowPersonalBankAccountOption}) {
const {translate} = useLocalize();
// Users can choose to pay with business bank account in case of Expense reports or in case of P2P IOU report
@@ -70,6 +74,8 @@ function AddPaymentMethodMenu({isVisible, onClose, anchorPosition, anchorAlignme
ReportUtils.isExpenseReport(iouReport) ||
(ReportUtils.isIOUReport(iouReport) && !ReportActionsUtils.hasRequestFromCurrentAccount(lodashGet(iouReport, 'reportID', 0), lodashGet(session, 'accountID', 0)));
+ const canUsePersonalBankAccount = shouldShowPersonalBankAccountOption || ReportUtils.isIOUReport(iouReport);
+
return (
{},
onKeyPress: () => {},
+ style: {},
+ containerStyles: {},
};
function AmountTextInput(props) {
const styles = useThemeStyles();
+ const StyleUtils = useStyleUtils();
return (
);
}
diff --git a/src/components/AnchorForAttachmentsOnly/index.native.js b/src/components/AnchorForAttachmentsOnly/index.native.js
index e3c7a71f2304..3277d51ec058 100644
--- a/src/components/AnchorForAttachmentsOnly/index.native.js
+++ b/src/components/AnchorForAttachmentsOnly/index.native.js
@@ -1,5 +1,5 @@
import React from 'react';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as anchorForAttachmentsOnlyPropTypes from './anchorForAttachmentsOnlyPropTypes';
import BaseAnchorForAttachmentsOnly from './BaseAnchorForAttachmentsOnly';
diff --git a/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly.js b/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly.js
index c056f68fa16c..4d32b4427d92 100644
--- a/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly.js
+++ b/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly.js
@@ -7,12 +7,12 @@ import _ from 'underscore';
import PressableWithSecondaryInteraction from '@components/PressableWithSecondaryInteraction';
import Text from '@components/Text';
import Tooltip from '@components/Tooltip';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import * as ContextMenuActions from '@pages/home/report/ContextMenu/ContextMenuActions';
import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import {propTypes as anchorForCommentsOnlyPropTypes} from './anchorForCommentsOnlyPropTypes';
diff --git a/src/components/AnimatedStep/index.tsx b/src/components/AnimatedStep/index.tsx
index 1a87592cba9b..e2b9952c0617 100644
--- a/src/components/AnimatedStep/index.tsx
+++ b/src/components/AnimatedStep/index.tsx
@@ -1,8 +1,8 @@
import React, {useMemo} from 'react';
import {StyleProp, ViewStyle} from 'react-native';
import * as Animatable from 'react-native-animatable';
+import useThemeStyles from '@hooks/useThemeStyles';
import useNativeDriver from '@libs/useNativeDriver';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import ChildrenProps from '@src/types/utils/ChildrenProps';
import {AnimationDirection} from './AnimatedStepContext';
diff --git a/src/components/AnonymousReportFooter.tsx b/src/components/AnonymousReportFooter.tsx
index 65dc813a829d..877ca9444661 100644
--- a/src/components/AnonymousReportFooter.tsx
+++ b/src/components/AnonymousReportFooter.tsx
@@ -3,7 +3,7 @@ import {Text, View} from 'react-native';
import {OnyxCollection} from 'react-native-onyx';
import {OnyxEntry} from 'react-native-onyx/lib/types';
import useLocalize from '@hooks/useLocalize';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as Session from '@userActions/Session';
import {PersonalDetails, Report} from '@src/types/onyx';
import AvatarWithDisplayName from './AvatarWithDisplayName';
diff --git a/src/components/ArchivedReportFooter.tsx b/src/components/ArchivedReportFooter.tsx
index 712ef6be769e..7dadd86debfe 100644
--- a/src/components/ArchivedReportFooter.tsx
+++ b/src/components/ArchivedReportFooter.tsx
@@ -2,10 +2,10 @@ import lodashEscape from 'lodash/escape';
import React from 'react';
import {OnyxEntry, withOnyx} from 'react-native-onyx';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {PersonalDetailsList, Report, ReportAction} from '@src/types/onyx';
diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js
index 79be536945ac..b1af96561ef5 100755
--- a/src/components/AttachmentModal.js
+++ b/src/components/AttachmentModal.js
@@ -4,10 +4,14 @@ import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
import React, {useCallback, useEffect, useMemo, useState} from 'react';
import {Animated, Keyboard, View} from 'react-native';
+import {GestureHandlerRootView} from 'react-native-gesture-handler';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import addEncryptedAuthTokenToURL from '@libs/addEncryptedAuthTokenToURL';
import compose from '@libs/compose';
@@ -19,9 +23,6 @@ import * as ReportUtils from '@libs/ReportUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
import useNativeDriver from '@libs/useNativeDriver';
import reportPropTypes from '@pages/reportPropTypes';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
@@ -425,78 +426,80 @@ function AttachmentModal(props) {
}}
propagateSwipe
>
- {props.isSmallScreenWidth && }
- downloadAttachment(source)}
- shouldShowCloseButton={!props.isSmallScreenWidth}
- shouldShowBackButton={props.isSmallScreenWidth}
- onBackButtonPress={closeModal}
- onCloseButtonPress={closeModal}
- shouldShowThreeDotsButton={shouldShowThreeDotsButton}
- threeDotsAnchorPosition={styles.threeDotsPopoverOffsetAttachmentModal(windowWidth)}
- threeDotsMenuItems={threeDotsMenuItems}
- shouldOverlay
- />
-
- {!_.isEmpty(props.report) && !props.isReceiptAttachment ? (
-
- ) : (
- Boolean(sourceForAttachmentView) &&
- shouldLoadAttachment && (
-
+ {props.isSmallScreenWidth && }
+ downloadAttachment(source)}
+ shouldShowCloseButton={!props.isSmallScreenWidth}
+ shouldShowBackButton={props.isSmallScreenWidth}
+ onBackButtonPress={closeModal}
+ onCloseButtonPress={closeModal}
+ shouldShowThreeDotsButton={shouldShowThreeDotsButton}
+ threeDotsAnchorPosition={styles.threeDotsPopoverOffsetAttachmentModal(windowWidth)}
+ threeDotsMenuItems={threeDotsMenuItems}
+ shouldOverlay
+ />
+
+ {!_.isEmpty(props.report) && !props.isReceiptAttachment ? (
+
- )
- )}
-
- {/* If we have an onConfirm method show a confirmation button */}
- {Boolean(props.onConfirm) && (
-
- {({safeAreaPaddingBottomStyle}) => (
-
-
-
+ )
)}
-
- )}
- {props.isReceiptAttachment && (
-
- )}
+
+ {/* If we have an onConfirm method show a confirmation button */}
+ {Boolean(props.onConfirm) && (
+
+ {({safeAreaPaddingBottomStyle}) => (
+
+
+
+ )}
+
+ )}
+ {props.isReceiptAttachment && (
+
+ )}
+
{!props.isReceiptAttachment && (
diff --git a/src/components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPage.js b/src/components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPage.js
deleted file mode 100644
index 7a083d71b591..000000000000
--- a/src/components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPage.js
+++ /dev/null
@@ -1,189 +0,0 @@
-/* eslint-disable es/no-optional-chaining */
-import PropTypes from 'prop-types';
-import React, {useContext, useEffect, useRef, useState} from 'react';
-import {ActivityIndicator, PixelRatio, StyleSheet, View} from 'react-native';
-import * as AttachmentsPropTypes from '@components/Attachments/propTypes';
-import Image from '@components/Image';
-import AttachmentCarouselPagerContext from './AttachmentCarouselPagerContext';
-import ImageTransformer from './ImageTransformer';
-import ImageWrapper from './ImageWrapper';
-
-function getCanvasFitScale({canvasWidth, canvasHeight, imageWidth, imageHeight}) {
- const imageScaleX = canvasWidth / imageWidth;
- const imageScaleY = canvasHeight / imageHeight;
-
- return {imageScaleX, imageScaleY};
-}
-
-const cachedDimensions = new Map();
-
-const pagePropTypes = {
- /** Whether source url requires authentication */
- isAuthTokenRequired: PropTypes.bool,
-
- /** URL to full-sized attachment, SVG function, or numeric static image on native platforms */
- source: AttachmentsPropTypes.attachmentSourcePropType.isRequired,
-
- isActive: PropTypes.bool.isRequired,
-};
-
-const defaultProps = {
- isAuthTokenRequired: false,
-};
-
-function AttachmentCarouselPage({source, isAuthTokenRequired, isActive: initialIsActive}) {
- const {canvasWidth, canvasHeight} = useContext(AttachmentCarouselPagerContext);
-
- const dimensions = cachedDimensions.get(source);
-
- const [isActive, setIsActive] = useState(initialIsActive);
- // We delay setting a page to active state by a (few) millisecond(s),
- // to prevent the image transformer from flashing while still rendering
- // Instead, we show the fallback image while the image transformer is loading the image
- useEffect(() => {
- if (initialIsActive) {
- setTimeout(() => setIsActive(true), 1);
- } else {
- setIsActive(false);
- }
- }, [initialIsActive]);
-
- const [initialActivePageLoad, setInitialActivePageLoad] = useState(isActive);
- const isImageLoaded = useRef(null);
- const [isImageLoading, setIsImageLoading] = useState(false);
- const [isFallbackLoading, setIsFallbackLoading] = useState(false);
- const [showFallback, setShowFallback] = useState(true);
-
- // We delay hiding the fallback image while image transformer is still rendering
- useEffect(() => {
- if (isImageLoading || showFallback) {
- setShowFallback(true);
- } else {
- setTimeout(() => setShowFallback(false), 100);
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [isImageLoading]);
-
- return (
- <>
- {isActive && (
-
-
- {
- setIsImageLoading(true);
- }}
- onLoadEnd={() => {
- setShowFallback(false);
- setIsImageLoading(false);
- isImageLoaded.current = true;
- }}
- onLoad={(evt) => {
- const imageWidth = (evt.nativeEvent?.width || 0) / PixelRatio.get();
- const imageHeight = (evt.nativeEvent?.height || 0) / PixelRatio.get();
-
- const {imageScaleX, imageScaleY} = getCanvasFitScale({canvasWidth, canvasHeight, imageWidth, imageHeight});
-
- // Don't update the dimensions if they are already set
- if (
- dimensions?.imageWidth !== imageWidth ||
- dimensions?.imageHeight !== imageHeight ||
- dimensions?.imageScaleX !== imageScaleX ||
- dimensions?.imageScaleY !== imageScaleY
- ) {
- cachedDimensions.set(source, {
- ...dimensions,
- imageWidth,
- imageHeight,
- imageScaleX,
- imageScaleY,
- });
- }
-
- // On the initial render of the active page, the onLoadEnd event is never fired.
- // That's why we instead set isImageLoading to false in the onLoad event.
- if (initialActivePageLoad) {
- setInitialActivePageLoad(false);
- setIsImageLoading(false);
- setTimeout(() => setShowFallback(false), 100);
- isImageLoaded.current = true;
- }
- }}
- />
-
-
- )}
-
- {/* Keep rendering the image without gestures as fallback while ImageTransformer is loading the image */}
- {(showFallback || !isActive) && (
-
- {
- setIsImageLoading(true);
- if (isImageLoaded.current) {
- return;
- }
- setIsFallbackLoading(true);
- }}
- onLoadEnd={() => {
- if (isImageLoaded.current) {
- return;
- }
- setIsFallbackLoading(false);
- }}
- onLoad={(evt) => {
- const imageWidth = evt.nativeEvent.width;
- const imageHeight = evt.nativeEvent.height;
-
- const {imageScaleX, imageScaleY} = getCanvasFitScale({canvasWidth, canvasHeight, imageWidth, imageHeight});
- const minImageScale = Math.min(imageScaleX, imageScaleY);
-
- const scaledImageWidth = imageWidth * minImageScale;
- const scaledImageHeight = imageHeight * minImageScale;
-
- // Don't update the dimensions if they are already set
- if (dimensions?.scaledImageWidth === scaledImageWidth && dimensions?.scaledImageHeight === scaledImageHeight) {
- return;
- }
-
- cachedDimensions.set(source, {
- ...dimensions,
- scaledImageWidth,
- scaledImageHeight,
- });
- }}
- style={dimensions == null ? undefined : {width: dimensions.scaledImageWidth, height: dimensions.scaledImageHeight}}
- />
-
- )}
-
- {/* Show activity indicator while ImageTransfomer is still loading the image. */}
- {isActive && isFallbackLoading && !isImageLoaded.current && (
-
- )}
- >
- );
-}
-
-AttachmentCarouselPage.propTypes = pagePropTypes;
-AttachmentCarouselPage.defaultProps = defaultProps;
-AttachmentCarouselPage.displayName = 'AttachmentCarouselPage';
-
-export default AttachmentCarouselPage;
diff --git a/src/components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPagerContext.js b/src/components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPagerContext.js
index 39535288e22d..abaf06900853 100644
--- a/src/components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPagerContext.js
+++ b/src/components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPagerContext.js
@@ -1,5 +1,5 @@
import {createContext} from 'react';
-const AttachmentCarouselContextPager = createContext(null);
+const AttachmentCarouselPagerContext = createContext(null);
-export default AttachmentCarouselContextPager;
+export default AttachmentCarouselPagerContext;
diff --git a/src/components/Attachments/AttachmentCarousel/Pager/ImageWrapper.js b/src/components/Attachments/AttachmentCarousel/Pager/ImageWrapper.js
deleted file mode 100644
index b0a8b1f0d083..000000000000
--- a/src/components/Attachments/AttachmentCarousel/Pager/ImageWrapper.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import PropTypes from 'prop-types';
-import React from 'react';
-import {StyleSheet} from 'react-native';
-import Animated from 'react-native-reanimated';
-import useThemeStyles from '@styles/useThemeStyles';
-
-const imageWrapperPropTypes = {
- children: PropTypes.node.isRequired,
-};
-
-function ImageWrapper({children}) {
- const styles = useThemeStyles();
- return (
-
- {children}
-
- );
-}
-
-ImageWrapper.propTypes = imageWrapperPropTypes;
-ImageWrapper.displayName = 'ImageWrapper';
-
-export default ImageWrapper;
diff --git a/src/components/Attachments/AttachmentCarousel/Pager/index.js b/src/components/Attachments/AttachmentCarousel/Pager/index.js
index 6913941ed726..553e963a3461 100644
--- a/src/components/Attachments/AttachmentCarousel/Pager/index.js
+++ b/src/components/Attachments/AttachmentCarousel/Pager/index.js
@@ -1,12 +1,12 @@
import PropTypes from 'prop-types';
-import React, {useImperativeHandle, useMemo, useRef, useState} from 'react';
+import React, {useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react';
import {View} from 'react-native';
-import {createNativeWrapper, GestureHandlerRootView} from 'react-native-gesture-handler';
+import {createNativeWrapper} from 'react-native-gesture-handler';
import PagerView from 'react-native-pager-view';
import Animated, {runOnJS, useAnimatedProps, useAnimatedReaction, useEvent, useHandler, useSharedValue} from 'react-native-reanimated';
import _ from 'underscore';
import refPropTypes from '@components/refPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import AttachmentCarouselPagerContext from './AttachmentCarouselPagerContext';
const AnimatedPagerView = Animated.createAnimatedComponent(createNativeWrapper(PagerView));
@@ -51,8 +51,6 @@ const pagerPropTypes = {
onSwipeDown: PropTypes.func,
onPinchGestureChange: PropTypes.func,
forwardedRef: refPropTypes,
- containerWidth: PropTypes.number.isRequired,
- containerHeight: PropTypes.number.isRequired,
};
const pagerDefaultProps = {
@@ -66,20 +64,7 @@ const pagerDefaultProps = {
forwardedRef: null,
};
-function AttachmentCarouselPager({
- items,
- renderItem,
- initialIndex,
- onPageSelected,
- onTap,
- onSwipe = noopWorklet,
- onSwipeSuccess,
- onSwipeDown,
- onPinchGestureChange,
- forwardedRef,
- containerWidth,
- containerHeight,
-}) {
+function AttachmentCarouselPager({items, renderItem, initialIndex, onPageSelected, onTap, onSwipe = noopWorklet, onSwipeSuccess, onSwipeDown, onPinchGestureChange, forwardedRef}) {
const styles = useThemeStyles();
const shouldPagerScroll = useSharedValue(true);
const pagerRef = useRef(null);
@@ -101,6 +86,11 @@ function AttachmentCarouselPager({
const [activePage, setActivePage] = useState(initialIndex);
+ useEffect(() => {
+ setActivePage(initialIndex);
+ activeIndex.value = initialIndex;
+ }, [activeIndex, initialIndex]);
+
// we use reanimated for this since onPageSelected is called
// in the middle of the pager animation
useAnimatedReaction(
@@ -128,8 +118,6 @@ function AttachmentCarouselPager({
const contextValue = useMemo(
() => ({
- canvasWidth: containerWidth,
- canvasHeight: containerHeight,
isScrolling,
pagerRef,
shouldPagerScroll,
@@ -139,33 +127,31 @@ function AttachmentCarouselPager({
onSwipeSuccess,
onSwipeDown,
}),
- [containerWidth, containerHeight, isScrolling, pagerRef, shouldPagerScroll, onPinchGestureChange, onTap, onSwipe, onSwipeSuccess, onSwipeDown],
+ [isScrolling, pagerRef, shouldPagerScroll, onPinchGestureChange, onTap, onSwipe, onSwipeSuccess, onSwipeDown],
);
return (
-
-
-
- {_.map(items, (item, index) => (
-
- {renderItem({item, index, isActive: index === activePage})}
-
- ))}
-
-
-
+
+
+ {_.map(items, (item, index) => (
+
+ {renderItem({item, index, isActive: index === activePage})}
+
+ ))}
+
+
);
}
diff --git a/src/components/Attachments/AttachmentCarousel/index.js b/src/components/Attachments/AttachmentCarousel/index.js
index fb31e32de91c..974bb92bf3c8 100644
--- a/src/components/Attachments/AttachmentCarousel/index.js
+++ b/src/components/Attachments/AttachmentCarousel/index.js
@@ -6,10 +6,10 @@ import BlockingView from '@components/BlockingViews/BlockingView';
import * as Illustrations from '@components/Icon/Illustrations';
import withLocalize from '@components/withLocalize';
import withWindowDimensions from '@components/withWindowDimensions';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import Navigation from '@libs/Navigation/Navigation';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
@@ -139,10 +139,11 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source,
setShouldShowArrows(!shouldShowArrows) : undefined}
/>
),
- [activeSource, canUseTouchScreen, setShouldShowArrows, shouldShowArrows],
+ [activeSource, attachments.length, canUseTouchScreen, setShouldShowArrows, shouldShowArrows],
);
return (
diff --git a/src/components/Attachments/AttachmentCarousel/index.native.js b/src/components/Attachments/AttachmentCarousel/index.native.js
index ea45509d6ce3..f5479b73abdb 100644
--- a/src/components/Attachments/AttachmentCarousel/index.native.js
+++ b/src/components/Attachments/AttachmentCarousel/index.native.js
@@ -1,13 +1,14 @@
import React, {useCallback, useEffect, useRef, useState} from 'react';
-import {Keyboard, PixelRatio, View} from 'react-native';
+import {Keyboard, View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import BlockingView from '@components/BlockingViews/BlockingView';
+import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
import * as Illustrations from '@components/Icon/Illustrations';
import withLocalize from '@components/withLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import ONYXKEYS from '@src/ONYXKEYS';
import {defaultProps, propTypes} from './attachmentCarouselPropTypes';
@@ -20,13 +21,11 @@ import useCarouselArrows from './useCarouselArrows';
function AttachmentCarousel({report, reportActions, parentReportActions, source, onNavigate, setDownloadButtonVisibility, translate, onClose}) {
const styles = useThemeStyles();
const pagerRef = useRef(null);
-
- const [containerDimensions, setContainerDimensions] = useState({width: 0, height: 0});
- const [page, setPage] = useState(0);
+ const [page, setPage] = useState();
const [attachments, setAttachments] = useState([]);
- const [activeSource, setActiveSource] = useState(source);
const [isPinchGestureRunning, setIsPinchGestureRunning] = useState(true);
const [shouldShowArrows, setShouldShowArrows, autoHideArrows, cancelAutoHideArrows] = useCarouselArrows();
+ const [activeSource, setActiveSource] = useState(source);
const compareImage = useCallback((attachment) => attachment.source === source, [source]);
@@ -95,61 +94,63 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source,
* @returns {JSX.Element}
*/
const renderItem = useCallback(
- ({item, isActive}) => (
+ ({item, index, isActive}) => (
setShouldShowArrows(!shouldShowArrows)}
/>
),
- [activeSource, setShouldShowArrows, shouldShowArrows],
+ [activeSource, attachments.length, page, setShouldShowArrows, shouldShowArrows],
);
return (
- setContainerDimensions({width: PixelRatio.roundToNearestPixel(nativeEvent.layout.width), height: PixelRatio.roundToNearestPixel(nativeEvent.layout.height)})
- }
onMouseEnter={() => setShouldShowArrows(true)}
onMouseLeave={() => setShouldShowArrows(false)}
>
- {page === -1 ? (
-
+ {page == null ? (
+
) : (
<>
- cycleThroughAttachments(-1)}
- onForward={() => cycleThroughAttachments(1)}
- autoHideArrow={autoHideArrows}
- cancelAutoHideArrow={cancelAutoHideArrows}
- />
-
- {containerDimensions.width > 0 && containerDimensions.height > 0 && (
- updatePage(newPage)}
- onPinchGestureChange={(newIsPinchGestureRunning) => {
- setIsPinchGestureRunning(newIsPinchGestureRunning);
- if (!newIsPinchGestureRunning && !shouldShowArrows) {
- setShouldShowArrows(true);
- }
- }}
- onSwipeDown={onClose}
- containerWidth={containerDimensions.width}
- containerHeight={containerDimensions.height}
- ref={pagerRef}
+ {page === -1 ? (
+
+ ) : (
+ <>
+ cycleThroughAttachments(-1)}
+ onForward={() => cycleThroughAttachments(1)}
+ autoHideArrow={autoHideArrows}
+ cancelAutoHideArrow={cancelAutoHideArrows}
+ />
+
+ updatePage(newPage)}
+ onPinchGestureChange={(newIsPinchGestureRunning) => {
+ setIsPinchGestureRunning(newIsPinchGestureRunning);
+ if (!newIsPinchGestureRunning && !shouldShowArrows) {
+ setShouldShowArrows(true);
+ }
+ }}
+ onSwipeDown={onClose}
+ ref={pagerRef}
+ />
+ >
)}
>
)}
diff --git a/src/components/Attachments/AttachmentView/AttachmentViewImage/index.js b/src/components/Attachments/AttachmentView/AttachmentViewImage/index.js
index 3b080e47e4d1..f53b993f6053 100755
--- a/src/components/Attachments/AttachmentView/AttachmentViewImage/index.js
+++ b/src/components/Attachments/AttachmentView/AttachmentViewImage/index.js
@@ -2,8 +2,8 @@ import React, {memo} from 'react';
import ImageView from '@components/ImageView';
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import {attachmentViewImageDefaultProps, attachmentViewImagePropTypes} from './propTypes';
@@ -12,17 +12,38 @@ const propTypes = {
...withLocalizePropTypes,
};
-function AttachmentViewImage({source, file, isAuthTokenRequired, loadComplete, onPress, isImage, onScaleChanged, translate, onError}) {
+function AttachmentViewImage({
+ source,
+ file,
+ isAuthTokenRequired,
+ isUsedInCarousel,
+ isSingleCarouselItem,
+ carouselItemIndex,
+ carouselActiveItemIndex,
+ isFocused,
+ loadComplete,
+ onPress,
+ onError,
+ isImage,
+ onScaleChanged,
+ translate,
+}) {
const styles = useThemeStyles();
const children = (
);
+
return onPress ? (
- ) : (
-
- );
-
- return onPress ? (
-
- {children}
-
- ) : (
- children
- );
-}
-
-AttachmentViewImage.propTypes = propTypes;
-AttachmentViewImage.defaultProps = attachmentViewImageDefaultProps;
-AttachmentViewImage.displayName = 'AttachmentViewImage';
-
-export default compose(memo, withLocalize)(AttachmentViewImage);
diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.android.js b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.android.js
index b9dd65e2716b..308d3cf2c0ba 100644
--- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.android.js
+++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.android.js
@@ -3,7 +3,7 @@ import {StyleSheet, View} from 'react-native';
import {Gesture, GestureDetector} from 'react-native-gesture-handler';
import Animated, {useSharedValue} from 'react-native-reanimated';
import AttachmentCarouselPagerContext from '@components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPagerContext';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import BaseAttachmentViewPdf from './BaseAttachmentViewPdf';
import {attachmentViewPdfDefaultProps, attachmentViewPdfPropTypes} from './propTypes';
diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js
index 6e1ed651ae06..94faa13fbb0f 100755
--- a/src/components/Attachments/AttachmentView/index.js
+++ b/src/components/Attachments/AttachmentView/index.js
@@ -12,13 +12,12 @@ import Text from '@components/Text';
import Tooltip from '@components/Tooltip';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import useNetwork from '@hooks/useNetwork';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import addEncryptedAuthTokenToURL from '@libs/addEncryptedAuthTokenToURL';
import compose from '@libs/compose';
import * as TransactionUtils from '@libs/TransactionUtils';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
-import cursor from '@styles/utilities/cursor';
import variables from '@styles/variables';
import ONYXKEYS from '@src/ONYXKEYS';
import AttachmentViewImage from './AttachmentViewImage';
@@ -64,7 +63,6 @@ function AttachmentView({
source,
file,
isAuthTokenRequired,
- isUsedInCarousel,
onPress,
shouldShowLoadingSpinnerIcon,
shouldShowDownloadIcon,
@@ -73,10 +71,14 @@ function AttachmentView({
onToggleKeyboard,
translate,
isFocused,
+ isUsedInCarousel,
+ isSingleCarouselItem,
+ carouselItemIndex,
+ carouselActiveItemIndex,
+ isUsedInAttachmentModal,
isWorkspaceAvatar,
fallbackSource,
transaction,
- isUsedInAttachmentModal,
}) {
const theme = useTheme();
const styles = useThemeStyles();
@@ -133,15 +135,16 @@ function AttachmentView({
!loadComplete && setLoadComplete(true)}
- errorLabelStyles={isUsedInAttachmentModal ? [styles.textLabel, styles.textLarge] : [cursor.cursorAuto]}
+ errorLabelStyles={isUsedInAttachmentModal ? [styles.textLabel, styles.textLarge] : [styles.cursorAuto]}
style={isUsedInAttachmentModal ? styles.imageModalPDF : styles.flex1}
/>
@@ -162,9 +165,12 @@ function AttachmentView({
source={imageError ? fallbackSource : source}
file={file}
isAuthTokenRequired={isAuthTokenRequired}
- isUsedInCarousel={isUsedInCarousel}
loadComplete={loadComplete}
isFocused={isFocused}
+ isUsedInCarousel={isUsedInCarousel}
+ isSingleCarouselItem={isSingleCarouselItem}
+ carouselItemIndex={carouselItemIndex}
+ carouselActiveItemIndex={carouselActiveItemIndex}
isImage={isImage}
onPress={onPress}
onScaleChanged={onScaleChanged}
diff --git a/src/components/Attachments/AttachmentView/propTypes.js b/src/components/Attachments/AttachmentView/propTypes.js
index 0c7c8814267f..286c903ccf5b 100644
--- a/src/components/Attachments/AttachmentView/propTypes.js
+++ b/src/components/Attachments/AttachmentView/propTypes.js
@@ -17,14 +17,23 @@ const attachmentViewPropTypes = {
/** Whether this AttachmentView is shown as part of a AttachmentCarousel */
isUsedInCarousel: PropTypes.bool,
+ /** When "isUsedInCarousel" is set to true, determines whether there is only one item in the carousel */
+ isSingleCarouselItem: PropTypes.bool,
+
+ /** Whether this AttachmentView is shown as part of an AttachmentModal */
+ isUsedInAttachmentModal: PropTypes.bool,
+
+ /** The index of the carousel item */
+ carouselItemIndex: PropTypes.number,
+
+ /** The index of the currently active carousel item */
+ carouselActiveItemIndex: PropTypes.number,
+
/** Function for handle on press */
onPress: PropTypes.func,
/** Handles scale changed event */
onScaleChanged: PropTypes.func,
-
- /** Whether this AttachmentView is shown as part of an AttachmentModal */
- isUsedInAttachmentModal: PropTypes.bool,
};
const attachmentViewDefaultProps = {
@@ -34,9 +43,13 @@ const attachmentViewDefaultProps = {
},
isFocused: false,
isUsedInCarousel: false,
+ isSingleCarouselItem: false,
+ carouselItemIndex: 0,
+ carouselActiveItemIndex: 0,
+ isSingleElement: false,
+ isUsedInAttachmentModal: false,
onPress: undefined,
onScaleChanged: () => {},
- isUsedInAttachmentModal: false,
};
export {attachmentViewPropTypes, attachmentViewDefaultProps};
diff --git a/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx b/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx
index 07db455968a3..c2320f7c0202 100644
--- a/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx
+++ b/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx
@@ -4,9 +4,10 @@ import {View} from 'react-native';
// We take ScrollView from this package to properly handle the scrolling of AutoCompleteSuggestions in chats since one scroll is nested inside another
import {ScrollView} from 'react-native-gesture-handler';
import Animated, {Easing, FadeOutDown, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
+import ColorSchemeWrapper from '@components/ColorSchemeWrapper';
import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
import viewForwardedRef from '@src/types/utils/viewForwardedRef';
import type {AutoCompleteSuggestionsProps, RenderSuggestionMenuItemProps} from './types';
@@ -84,18 +85,20 @@ function BaseAutoCompleteSuggestions(
style={[styles.autoCompleteSuggestionsContainer, animatedStyles]}
exiting={FadeOutDown.duration(100).easing(Easing.inOut(Easing.ease))}
>
- rowHeight.value}
- extraData={highlightedSuggestionIndex}
- />
+
+ rowHeight.value}
+ extraData={highlightedSuggestionIndex}
+ />
+
);
}
diff --git a/src/components/AutoCompleteSuggestions/index.tsx b/src/components/AutoCompleteSuggestions/index.tsx
index 3ccbb4efaf5a..baca4011a177 100644
--- a/src/components/AutoCompleteSuggestions/index.tsx
+++ b/src/components/AutoCompleteSuggestions/index.tsx
@@ -1,9 +1,9 @@
import React from 'react';
import ReactDOM from 'react-dom';
import {View} from 'react-native';
+import useStyleUtils from '@hooks/useStyleUtils';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
-import useStyleUtils from '@styles/useStyleUtils';
import BaseAutoCompleteSuggestions from './BaseAutoCompleteSuggestions';
import type {AutoCompleteSuggestionsProps} from './types';
diff --git a/src/components/AutoEmailLink.js b/src/components/AutoEmailLink.js
index bffd2493aa5d..af581525ab69 100644
--- a/src/components/AutoEmailLink.js
+++ b/src/components/AutoEmailLink.js
@@ -2,7 +2,7 @@ import {CONST} from 'expensify-common/lib/CONST';
import PropTypes from 'prop-types';
import React from 'react';
import _ from 'underscore';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import Text from './Text';
import TextLink from './TextLink';
diff --git a/src/components/AutoUpdateTime.js b/src/components/AutoUpdateTime.tsx
similarity index 62%
rename from src/components/AutoUpdateTime.js
rename to src/components/AutoUpdateTime.tsx
index 1970839ec320..258bdb281eb2 100644
--- a/src/components/AutoUpdateTime.js
+++ b/src/components/AutoUpdateTime.tsx
@@ -2,42 +2,35 @@
* Displays the user's local time and updates it every minute.
* The time auto-update logic is extracted to this component to avoid re-rendering a more complex component, e.g. DetailsPage.
*/
-import PropTypes from 'prop-types';
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import {View} from 'react-native';
+import useThemeStyles from '@hooks/useThemeStyles';
import DateUtils from '@libs/DateUtils';
-import useThemeStyles from '@styles/useThemeStyles';
+import {Timezone} from '@src/types/onyx/PersonalDetails';
import Text from './Text';
-import withLocalize, {withLocalizePropTypes} from './withLocalize';
+import withLocalize, {WithLocalizeProps} from './withLocalize';
-const propTypes = {
+type AutoUpdateTimeProps = WithLocalizeProps & {
/** Timezone of the user from their personal details */
- timezone: PropTypes.shape({
- /** Value of selected timezone */
- selected: PropTypes.string,
-
- /** Whether timezone is automatically set */
- automatic: PropTypes.bool,
- }).isRequired,
- ...withLocalizePropTypes,
+ timezone: Timezone;
};
-function AutoUpdateTime(props) {
+function AutoUpdateTime({timezone, preferredLocale, translate}: AutoUpdateTimeProps) {
const styles = useThemeStyles();
- /**
- * @returns {Date} Returns the locale Date object
- */
- const getCurrentUserLocalTime = useCallback(
- () => DateUtils.getLocalDateFromDatetime(props.preferredLocale, null, props.timezone.selected),
- [props.preferredLocale, props.timezone.selected],
- );
+ /** @returns Returns the locale Date object */
+ const getCurrentUserLocalTime = useCallback(() => DateUtils.getLocalDateFromDatetime(preferredLocale, undefined, timezone.selected), [preferredLocale, timezone.selected]);
const [currentUserLocalTime, setCurrentUserLocalTime] = useState(getCurrentUserLocalTime);
const minuteRef = useRef(new Date().getMinutes());
- const timezoneName = useMemo(() => DateUtils.getZoneAbbreviation(currentUserLocalTime, props.timezone.selected), [currentUserLocalTime, props.timezone.selected]);
+ const timezoneName = useMemo(() => {
+ if (timezone.selected) {
+ return DateUtils.getZoneAbbreviation(currentUserLocalTime, timezone.selected);
+ }
+ return '';
+ }, [currentUserLocalTime, timezone.selected]);
useEffect(() => {
- // If the any of the props that getCurrentUserLocalTime depends on change, we want to update the displayed time immediately
+ // If any of the props that getCurrentUserLocalTime depends on change, we want to update the displayed time immediately
setCurrentUserLocalTime(getCurrentUserLocalTime());
// Also, if the user leaves this page open, we want to make sure the displayed time is updated every minute when the clock changes
@@ -58,7 +51,7 @@ function AutoUpdateTime(props) {
style={[styles.textLabelSupporting, styles.mb1]}
numberOfLines={1}
>
- {props.translate('detailsPage.localTime')}
+ {translate('detailsPage.localTime')}
{DateUtils.formatToLocalTime(currentUserLocalTime)} {timezoneName}
@@ -67,6 +60,5 @@ function AutoUpdateTime(props) {
);
}
-AutoUpdateTime.propTypes = propTypes;
AutoUpdateTime.displayName = 'AutoUpdateTime';
export default withLocalize(AutoUpdateTime);
diff --git a/src/components/Avatar.tsx b/src/components/Avatar.tsx
index f801cb11e9df..978d2f097cbf 100644
--- a/src/components/Avatar.tsx
+++ b/src/components/Avatar.tsx
@@ -1,11 +1,11 @@
import React, {useEffect, useState} from 'react';
import {StyleProp, View, ViewStyle} from 'react-native';
import useNetwork from '@hooks/useNetwork';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as ReportUtils from '@libs/ReportUtils';
import {AvatarSource} from '@libs/UserUtils';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import type {AvatarSizeName} from '@styles/utils';
import CONST from '@src/CONST';
import {AvatarType} from '@src/types/onyx/OnyxCommon';
diff --git a/src/components/AvatarCropModal/AvatarCropModal.js b/src/components/AvatarCropModal/AvatarCropModal.js
index 419891d9bdef..a39daeb78ba7 100644
--- a/src/components/AvatarCropModal/AvatarCropModal.js
+++ b/src/components/AvatarCropModal/AvatarCropModal.js
@@ -15,11 +15,11 @@ import Text from '@components/Text';
import Tooltip from '@components/Tooltip';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import cropOrRotateImage from '@libs/cropOrRotateImage';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import ImageCropView from './ImageCropView';
import Slider from './Slider';
diff --git a/src/components/AvatarCropModal/ImageCropView.js b/src/components/AvatarCropModal/ImageCropView.js
index 94289b24d6ca..3f6263cf7b95 100644
--- a/src/components/AvatarCropModal/ImageCropView.js
+++ b/src/components/AvatarCropModal/ImageCropView.js
@@ -5,9 +5,9 @@ import {PanGestureHandler} from 'react-native-gesture-handler';
import Animated, {interpolate, useAnimatedStyle} from 'react-native-reanimated';
import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import ControlSelection from '@libs/ControlSelection';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import gestureHandlerPropTypes from './gestureHandlerPropTypes';
const propTypes = {
diff --git a/src/components/AvatarCropModal/Slider.js b/src/components/AvatarCropModal/Slider.js
index 9df6ac3c0498..ba2e1471ce9e 100644
--- a/src/components/AvatarCropModal/Slider.js
+++ b/src/components/AvatarCropModal/Slider.js
@@ -5,8 +5,8 @@ import {PanGestureHandler} from 'react-native-gesture-handler';
import Animated, {useAnimatedStyle} from 'react-native-reanimated';
import Tooltip from '@components/Tooltip';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import ControlSelection from '@libs/ControlSelection';
-import useThemeStyles from '@styles/useThemeStyles';
import gestureHandlerPropTypes from './gestureHandlerPropTypes';
const propTypes = {
diff --git a/src/components/AvatarSkeleton.tsx b/src/components/AvatarSkeleton.tsx
index d2706447f756..0887830aa07a 100644
--- a/src/components/AvatarSkeleton.tsx
+++ b/src/components/AvatarSkeleton.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import {Circle} from 'react-native-svg';
-import useTheme from '@styles/themes/useTheme';
+import useTheme from '@hooks/useTheme';
import SkeletonViewContentLoader from './SkeletonViewContentLoader';
function AvatarSkeleton() {
diff --git a/src/components/AvatarWithDisplayName.tsx b/src/components/AvatarWithDisplayName.tsx
index 3cc39f9e815f..5ea21502f2ca 100644
--- a/src/components/AvatarWithDisplayName.tsx
+++ b/src/components/AvatarWithDisplayName.tsx
@@ -2,12 +2,12 @@ import React, {useCallback, useEffect, useRef} from 'react';
import {View} from 'react-native';
import {OnyxCollection, OnyxEntry, withOnyx} from 'react-native-onyx';
import {ValueOf} from 'type-fest';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as ReportUtils from '@libs/ReportUtils';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
diff --git a/src/components/AvatarWithImagePicker.js b/src/components/AvatarWithImagePicker.js
index 9b061ba5c670..325d4706da71 100644
--- a/src/components/AvatarWithImagePicker.js
+++ b/src/components/AvatarWithImagePicker.js
@@ -4,12 +4,12 @@ import React, {useEffect, useRef, useState} from 'react';
import {StyleSheet, View} from 'react-native';
import _ from 'underscore';
import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as Browser from '@libs/Browser';
import * as FileUtils from '@libs/fileDownload/FileUtils';
import getImageResolution from '@libs/fileDownload/getImageResolution';
import stylePropTypes from '@styles/stylePropTypes';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import AttachmentModal from './AttachmentModal';
diff --git a/src/components/AvatarWithIndicator.tsx b/src/components/AvatarWithIndicator.tsx
index 3ae9507350c8..2fd733d4b072 100644
--- a/src/components/AvatarWithIndicator.tsx
+++ b/src/components/AvatarWithIndicator.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import {View} from 'react-native';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as UserUtils from '@libs/UserUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import Avatar from './Avatar';
import AvatarSkeleton from './AvatarSkeleton';
import * as Expensicons from './Icon/Expensicons';
diff --git a/src/components/Badge.tsx b/src/components/Badge.tsx
index b670921dff4c..70aebc30ee83 100644
--- a/src/components/Badge.tsx
+++ b/src/components/Badge.tsx
@@ -1,7 +1,7 @@
import React, {useCallback} from 'react';
import {GestureResponderEvent, PressableStateCallbackType, StyleProp, TextStyle, View, ViewStyle} from 'react-native';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback';
import Text from './Text';
diff --git a/src/components/Banner.tsx b/src/components/Banner.tsx
index 3eb2316aa9d0..7ceb3deb9e63 100644
--- a/src/components/Banner.tsx
+++ b/src/components/Banner.tsx
@@ -1,9 +1,9 @@
import React, {memo} from 'react';
import {StyleProp, TextStyle, View, ViewStyle} from 'react-native';
import useLocalize from '@hooks/useLocalize';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import getButtonState from '@libs/getButtonState';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import Hoverable from './Hoverable';
import Icon from './Icon';
diff --git a/src/components/BaseMiniContextMenuItem.tsx b/src/components/BaseMiniContextMenuItem.tsx
index 082c0e20801a..8d115a37cba7 100644
--- a/src/components/BaseMiniContextMenuItem.tsx
+++ b/src/components/BaseMiniContextMenuItem.tsx
@@ -1,10 +1,10 @@
import React, {ForwardedRef} from 'react';
import {PressableStateCallbackType, View} from 'react-native';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import DomUtils from '@libs/DomUtils';
import getButtonState from '@libs/getButtonState';
import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback';
import Tooltip from './Tooltip/PopoverAnchorTooltip';
diff --git a/src/components/BigNumberPad.tsx b/src/components/BigNumberPad.tsx
index 812e9e78635b..8b840f9d1b57 100644
--- a/src/components/BigNumberPad.tsx
+++ b/src/components/BigNumberPad.tsx
@@ -1,9 +1,9 @@
import React, {useState} from 'react';
import {View} from 'react-native';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import ControlSelection from '@libs/ControlSelection';
-import useThemeStyles from '@styles/useThemeStyles';
import Button from './Button';
type BigNumberPadProps = {
@@ -15,6 +15,9 @@ type BigNumberPadProps = {
/** Used to locate this view from native classes. */
id?: string;
+
+ /** Whether long press is disabled */
+ isLongPressDisabled: boolean;
};
const padNumbers = [
@@ -24,7 +27,7 @@ const padNumbers = [
['.', '0', '<'],
] as const;
-function BigNumberPad({numberPressed, longPressHandlerStateChanged = () => {}, id = 'numPadView'}: BigNumberPadProps) {
+function BigNumberPad({numberPressed, longPressHandlerStateChanged = () => {}, id = 'numPadView', isLongPressDisabled = false}: BigNumberPadProps) {
const {toLocaleDigit} = useLocalize();
const styles = useThemeStyles();
@@ -85,6 +88,7 @@ function BigNumberPad({numberPressed, longPressHandlerStateChanged = () => {}, i
onMouseDown={(e) => {
e.preventDefault();
}}
+ isLongPressDisabled={isLongPressDisabled}
/>
);
})}
diff --git a/src/components/BlockingViews/BlockingView.js b/src/components/BlockingViews/BlockingView.js
index aec414cdeb74..44f6b7100509 100644
--- a/src/components/BlockingViews/BlockingView.js
+++ b/src/components/BlockingViews/BlockingView.js
@@ -6,9 +6,9 @@ import Icon from '@components/Icon';
import Text from '@components/Text';
import TextLink from '@components/TextLink';
import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
const propTypes = {
diff --git a/src/components/BlockingViews/FullPageNotFoundView.js b/src/components/BlockingViews/FullPageNotFoundView.js
index b82474aa0694..ce76b96c0eb0 100644
--- a/src/components/BlockingViews/FullPageNotFoundView.js
+++ b/src/components/BlockingViews/FullPageNotFoundView.js
@@ -4,8 +4,8 @@ import {View} from 'react-native';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import * as Illustrations from '@components/Icon/Illustrations';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import ROUTES from '@src/ROUTES';
import BlockingView from './BlockingView';
diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx
index eb99d4b09396..af8ec33683de 100644
--- a/src/components/Button/index.tsx
+++ b/src/components/Button/index.tsx
@@ -9,9 +9,9 @@ import Text from '@components/Text';
import withNavigationFallback from '@components/withNavigationFallback';
import useActiveElement from '@hooks/useActiveElement';
import useKeyboardShortcut from '@hooks/useKeyboardShortcut';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import HapticFeedback from '@libs/HapticFeedback';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import ChildrenProps from '@src/types/utils/ChildrenProps';
import validateSubmitShortcut from './validateSubmitShortcut';
@@ -106,6 +106,9 @@ type ButtonProps = (ButtonWithText | ChildrenProps) & {
/** Should enable the haptic feedback? */
shouldEnableHapticFeedback?: boolean;
+ /** Should disable the long press? */
+ isLongPressDisabled?: boolean;
+
/** Id to use for this button */
id?: string;
@@ -149,6 +152,7 @@ function Button(
shouldRemoveRightBorderRadius = false,
shouldRemoveLeftBorderRadius = false,
shouldEnableHapticFeedback = false,
+ isLongPressDisabled = false,
id = '',
accessibilityLabel = '',
@@ -255,6 +259,9 @@ function Button(
return onPress(event);
}}
onLongPress={(event) => {
+ if (isLongPressDisabled) {
+ return;
+ }
if (shouldEnableHapticFeedback) {
HapticFeedback.longPress();
}
diff --git a/src/components/ButtonWithDropdownMenu.js b/src/components/ButtonWithDropdownMenu.js
index a5f311740f19..0291143a9dbc 100644
--- a/src/components/ButtonWithDropdownMenu.js
+++ b/src/components/ButtonWithDropdownMenu.js
@@ -2,10 +2,10 @@ import PropTypes from 'prop-types';
import React, {useEffect, useRef, useState} from 'react';
import {View} from 'react-native';
import _ from 'underscore';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import Button from './Button';
import Icon from './Icon';
diff --git a/src/components/CardPreview.tsx b/src/components/CardPreview.tsx
index 6dc8abfb80ef..e3ddbd54b892 100644
--- a/src/components/CardPreview.tsx
+++ b/src/components/CardPreview.tsx
@@ -3,7 +3,7 @@ import {View} from 'react-native';
import {OnyxEntry, withOnyx} from 'react-native-onyx';
import ExpensifyCardImage from '@assets/images/expensify-card.svg';
import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
import ONYXKEYS from '@src/ONYXKEYS';
import {PrivatePersonalDetails, Session} from '@src/types/onyx';
diff --git a/src/components/CategoryPicker/index.js b/src/components/CategoryPicker/index.js
index 36cf9b1deadc..d170def12276 100644
--- a/src/components/CategoryPicker/index.js
+++ b/src/components/CategoryPicker/index.js
@@ -4,8 +4,8 @@ import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import OptionsSelector from '@components/OptionsSelector';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as OptionsListUtils from '@libs/OptionsListUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import {defaultProps, propTypes} from './categoryPickerPropTypes';
diff --git a/src/components/Checkbox.tsx b/src/components/Checkbox.tsx
index 5dd3164eadcc..23bc068e8fe0 100644
--- a/src/components/Checkbox.tsx
+++ b/src/components/Checkbox.tsx
@@ -1,8 +1,8 @@
import React, {ForwardedRef, forwardRef, KeyboardEvent as ReactKeyboardEvent} from 'react';
import {GestureResponderEvent, StyleProp, View, ViewStyle} from 'react-native';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
import ChildrenProps from '@src/types/utils/ChildrenProps';
import Icon from './Icon';
diff --git a/src/components/CheckboxWithLabel.js b/src/components/CheckboxWithLabel.js
index 92cd7ea38eea..24f61c305dda 100644
--- a/src/components/CheckboxWithLabel.js
+++ b/src/components/CheckboxWithLabel.js
@@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
import React, {useState} from 'react';
import {View} from 'react-native';
import _ from 'underscore';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
import Checkbox from './Checkbox';
import FormHelpMessage from './FormHelpMessage';
diff --git a/src/components/CollapsibleSection/index.tsx b/src/components/CollapsibleSection/index.tsx
index 04574c5fd057..bc7bf6e89dba 100644
--- a/src/components/CollapsibleSection/index.tsx
+++ b/src/components/CollapsibleSection/index.tsx
@@ -4,7 +4,7 @@ import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
import Text from '@components/Text';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
import ChildrenProps from '@src/types/utils/ChildrenProps';
import Collapsible from './Collapsible';
diff --git a/src/components/ColorSchemeWrapper/index.tsx b/src/components/ColorSchemeWrapper/index.tsx
index 2909f1ffbe9f..0a1ccc5e5d67 100644
--- a/src/components/ColorSchemeWrapper/index.tsx
+++ b/src/components/ColorSchemeWrapper/index.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import {View} from 'react-native';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
function ColorSchemeWrapper({children}: React.PropsWithChildren): React.ReactElement {
const theme = useTheme();
diff --git a/src/components/CommunicationsLink.js b/src/components/CommunicationsLink.js
index dbbe5737b3aa..01ae0354a66d 100644
--- a/src/components/CommunicationsLink.js
+++ b/src/components/CommunicationsLink.js
@@ -1,8 +1,8 @@
import PropTypes from 'prop-types';
import React from 'react';
import {View} from 'react-native';
+import useThemeStyles from '@hooks/useThemeStyles';
import Clipboard from '@libs/Clipboard';
-import useThemeStyles from '@styles/useThemeStyles';
import ContextMenuItem from './ContextMenuItem';
import * as Expensicons from './Icon/Expensicons';
import withLocalize, {withLocalizePropTypes} from './withLocalize';
diff --git a/src/components/Composer/index.android.js b/src/components/Composer/index.android.js
index 698c68cc78e9..af64831df117 100644
--- a/src/components/Composer/index.android.js
+++ b/src/components/Composer/index.android.js
@@ -3,9 +3,9 @@ import React, {useCallback, useEffect, useMemo, useRef} from 'react';
import {StyleSheet} from 'react-native';
import _ from 'underscore';
import RNTextInput from '@components/RNTextInput';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as ComposerUtils from '@libs/ComposerUtils';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
const propTypes = {
/** Maximum number of lines in the text input */
diff --git a/src/components/Composer/index.ios.js b/src/components/Composer/index.ios.js
index 9852e607562b..c9947999b273 100644
--- a/src/components/Composer/index.ios.js
+++ b/src/components/Composer/index.ios.js
@@ -3,9 +3,9 @@ import React, {useCallback, useEffect, useMemo, useRef} from 'react';
import {StyleSheet} from 'react-native';
import _ from 'underscore';
import RNTextInput from '@components/RNTextInput';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as ComposerUtils from '@libs/ComposerUtils';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
const propTypes = {
/** If the input should clear, it actually gets intercepted instead of .clear() */
diff --git a/src/components/Composer/index.js b/src/components/Composer/index.js
index 4bb3df5c1b85..3af22b63ed69 100755
--- a/src/components/Composer/index.js
+++ b/src/components/Composer/index.js
@@ -8,6 +8,10 @@ import RNTextInput from '@components/RNTextInput';
import Text from '@components/Text';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import withNavigation from '@components/withNavigation';
+import useIsScrollBarVisible from '@hooks/useIsScrollBarVisible';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as Browser from '@libs/Browser';
import compose from '@libs/compose';
@@ -15,9 +19,6 @@ import * as ComposerUtils from '@libs/ComposerUtils';
import updateIsFullComposerAvailable from '@libs/ComposerUtils/updateIsFullComposerAvailable';
import isEnterWhileComposition from '@libs/KeyboardShortcut/isEnterWhileComposition';
import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
const propTypes = {
@@ -86,6 +87,9 @@ const propTypes = {
/** Whether the sull composer is open */
isComposerFullSize: PropTypes.bool,
+ /** Should make the input only scroll inside the element avoid scroll out to parent */
+ shouldContainScroll: PropTypes.bool,
+
...withLocalizePropTypes,
};
@@ -113,6 +117,7 @@ const defaultProps = {
checkComposerVisibility: () => false,
isReportActionCompose: false,
isComposerFullSize: false,
+ shouldContainScroll: false,
};
/**
@@ -164,6 +169,7 @@ function Composer({
selection: selectionProp,
isReportActionCompose,
isComposerFullSize,
+ shouldContainScroll,
...props
}) {
const theme = useTheme();
@@ -180,6 +186,7 @@ function Composer({
const [caretContent, setCaretContent] = useState('');
const [valueBeforeCaret, setValueBeforeCaret] = useState('');
const [textInputWidth, setTextInputWidth] = useState('');
+ const isScrollBarVisible = useIsScrollBarVisible(textInput, value);
useEffect(() => {
if (!shouldClear) {
@@ -418,18 +425,26 @@ function Composer({
);
- const inputStyleMemo = useMemo(
- () => [
+ const scrollStyleMemo = useMemo(() => {
+ if (shouldContainScroll) {
+ return isScrollBarVisible ? [styles.overflowScroll, styles.overscrollBehaviorContain] : styles.overflowHidden;
+ }
+ return [
// We are hiding the scrollbar to prevent it from reducing the text input width,
// so we can get the correct scroll height while calculating the number of lines.
numberOfLines < maxLines ? styles.overflowHidden : {},
+ ];
+ }, [shouldContainScroll, isScrollBarVisible, maxLines, numberOfLines, styles.overflowHidden, styles.overflowScroll, styles.overscrollBehaviorContain]);
+ const inputStyleMemo = useMemo(
+ () => [
StyleSheet.flatten([style, {outline: 'none'}]),
StyleUtils.getComposeTextAreaPadding(numberOfLines, isComposerFullSize),
Browser.isMobileSafari() || Browser.isSafari() ? styles.rtlTextRenderForSafari : {},
+ scrollStyleMemo,
],
- [numberOfLines, maxLines, styles.overflowHidden, styles.rtlTextRenderForSafari, style, StyleUtils, isComposerFullSize],
+ [numberOfLines, scrollStyleMemo, styles.rtlTextRenderForSafari, style, StyleUtils, isComposerFullSize],
);
return (
diff --git a/src/components/ConfirmContent.js b/src/components/ConfirmContent.js
index ff8ee4f861a4..8574cc02014e 100644
--- a/src/components/ConfirmContent.js
+++ b/src/components/ConfirmContent.js
@@ -4,7 +4,7 @@ import {View} from 'react-native';
import _ from 'underscore';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
import Button from './Button';
import Header from './Header';
diff --git a/src/components/ConfirmationPage.tsx b/src/components/ConfirmationPage.tsx
index 12e8b40a0f25..21813edd693d 100644
--- a/src/components/ConfirmationPage.tsx
+++ b/src/components/ConfirmationPage.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import {View} from 'react-native';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import Button from './Button';
import FixedFooter from './FixedFooter';
import Lottie from './Lottie';
diff --git a/src/components/ConfirmedRoute.js b/src/components/ConfirmedRoute.js
index 79b97b38194a..3abec6d0d1f4 100644
--- a/src/components/ConfirmedRoute.js
+++ b/src/components/ConfirmedRoute.js
@@ -5,9 +5,9 @@ import React, {useCallback, useEffect} from 'react';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import useNetwork from '@hooks/useNetwork';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as TransactionUtils from '@libs/TransactionUtils';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import * as MapboxToken from '@userActions/MapboxToken';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/components/ConnectBankAccountButton.js b/src/components/ConnectBankAccountButton.js
index 6afd3d57d4e6..f036918d9429 100644
--- a/src/components/ConnectBankAccountButton.js
+++ b/src/components/ConnectBankAccountButton.js
@@ -1,9 +1,9 @@
import PropTypes from 'prop-types';
import React from 'react';
import {View} from 'react-native';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
-import useThemeStyles from '@styles/useThemeStyles';
import * as ReimbursementAccount from '@userActions/ReimbursementAccount';
import Button from './Button';
import * as Expensicons from './Icon/Expensicons';
diff --git a/src/components/ContextMenuItem.js b/src/components/ContextMenuItem.js
index 2cabd71b11cb..307cfcde9b10 100644
--- a/src/components/ContextMenuItem.js
+++ b/src/components/ContextMenuItem.js
@@ -1,10 +1,10 @@
import PropTypes from 'prop-types';
import React, {forwardRef, useImperativeHandle} from 'react';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import useThrottledButtonState from '@hooks/useThrottledButtonState';
import useWindowDimensions from '@hooks/useWindowDimensions';
import getButtonState from '@libs/getButtonState';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import BaseMiniContextMenuItem from './BaseMiniContextMenuItem';
import Icon from './Icon';
import MenuItem from './MenuItem';
diff --git a/src/components/CopyTextToClipboard.js b/src/components/CopyTextToClipboard.js
deleted file mode 100644
index 678537c6a3d7..000000000000
--- a/src/components/CopyTextToClipboard.js
+++ /dev/null
@@ -1,48 +0,0 @@
-import PropTypes from 'prop-types';
-import React, {useCallback} from 'react';
-import Clipboard from '@libs/Clipboard';
-import * as Expensicons from './Icon/Expensicons';
-import PressableWithDelayToggle from './Pressable/PressableWithDelayToggle';
-import withLocalize, {withLocalizePropTypes} from './withLocalize';
-
-const propTypes = {
- /** The text to display and copy to the clipboard */
- text: PropTypes.string.isRequired,
-
- /** Styles to apply to the text */
- // eslint-disable-next-line react/forbid-prop-types
- textStyles: PropTypes.arrayOf(PropTypes.object),
- urlToCopy: PropTypes.string,
- accessibilityRole: PropTypes.string,
- ...withLocalizePropTypes,
-};
-
-const defaultProps = {
- textStyles: [],
- urlToCopy: null,
- accessibilityRole: undefined,
-};
-
-function CopyTextToClipboard(props) {
- const copyToClipboard = useCallback(() => {
- Clipboard.setString(props.urlToCopy || props.text);
- }, [props.text, props.urlToCopy]);
-
- return (
-
- );
-}
-
-CopyTextToClipboard.propTypes = propTypes;
-CopyTextToClipboard.defaultProps = defaultProps;
-CopyTextToClipboard.displayName = 'CopyTextToClipboard';
-
-export default withLocalize(CopyTextToClipboard);
diff --git a/src/components/CopyTextToClipboard.tsx b/src/components/CopyTextToClipboard.tsx
new file mode 100644
index 000000000000..6f3b42e88fee
--- /dev/null
+++ b/src/components/CopyTextToClipboard.tsx
@@ -0,0 +1,45 @@
+import React, {useCallback} from 'react';
+import {AccessibilityRole, StyleProp, TextStyle} from 'react-native';
+import useLocalize from '@hooks/useLocalize';
+import Clipboard from '@libs/Clipboard';
+import * as Expensicons from './Icon/Expensicons';
+import PressableWithDelayToggle from './Pressable/PressableWithDelayToggle';
+
+type CopyTextToClipboardProps = {
+ /** The text to display and copy to the clipboard */
+ text: string;
+
+ /** Styles to apply to the text */
+ textStyles?: StyleProp;
+
+ urlToCopy?: string;
+
+ accessibilityRole?: AccessibilityRole;
+};
+
+function CopyTextToClipboard({text, textStyles, urlToCopy, accessibilityRole}: CopyTextToClipboardProps) {
+ const {translate} = useLocalize();
+
+ const copyToClipboard = useCallback(() => {
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- nullish coalescing doesn't achieve the same result in this case
+ Clipboard.setString(urlToCopy || text);
+ }, [text, urlToCopy]);
+
+ return (
+
+ );
+}
+
+CopyTextToClipboard.displayName = 'CopyTextToClipboard';
+
+export default CopyTextToClipboard;
diff --git a/src/components/CountrySelector.js b/src/components/CountrySelector.js
index b138bc949937..68a6486bce48 100644
--- a/src/components/CountrySelector.js
+++ b/src/components/CountrySelector.js
@@ -2,8 +2,8 @@ import PropTypes from 'prop-types';
import React, {useEffect} from 'react';
import {View} from 'react-native';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
-import useThemeStyles from '@styles/useThemeStyles';
import ROUTES from '@src/ROUTES';
import FormHelpMessage from './FormHelpMessage';
import MenuItemWithTopDescription from './MenuItemWithTopDescription';
diff --git a/src/components/CurrencySymbolButton.js b/src/components/CurrencySymbolButton.js
index 47c25a43ad11..d03834fc1fd6 100644
--- a/src/components/CurrencySymbolButton.js
+++ b/src/components/CurrencySymbolButton.js
@@ -1,7 +1,7 @@
import PropTypes from 'prop-types';
import React from 'react';
import useLocalize from '@hooks/useLocalize';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback';
import Text from './Text';
diff --git a/src/components/CurrentUserPersonalDetailsSkeletonView/index.tsx b/src/components/CurrentUserPersonalDetailsSkeletonView/index.tsx
index 6f6daddc51b8..02c308705994 100644
--- a/src/components/CurrentUserPersonalDetailsSkeletonView/index.tsx
+++ b/src/components/CurrentUserPersonalDetailsSkeletonView/index.tsx
@@ -3,9 +3,9 @@ import {View} from 'react-native';
import {Circle, Rect} from 'react-native-svg';
import {ValueOf} from 'type-fest';
import SkeletonViewContentLoader from '@components/SkeletonViewContentLoader';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
+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';
diff --git a/src/components/CurrentWalletBalance.tsx b/src/components/CurrentWalletBalance.tsx
index 9a5b2cd0227b..28a83fb1ae50 100644
--- a/src/components/CurrentWalletBalance.tsx
+++ b/src/components/CurrentWalletBalance.tsx
@@ -1,8 +1,8 @@
import React from 'react';
import {StyleProp, TextStyle} from 'react-native';
import {OnyxEntry, withOnyx} from 'react-native-onyx';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as CurrencyUtils from '@libs/CurrencyUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import ONYXKEYS from '@src/ONYXKEYS';
import UserWallet from '@src/types/onyx/UserWallet';
import Text from './Text';
diff --git a/src/components/CustomStatusBar/CustomStatusBarContext.tsx b/src/components/CustomStatusBar/CustomStatusBarContext.tsx
deleted file mode 100644
index b2c317b05c25..000000000000
--- a/src/components/CustomStatusBar/CustomStatusBarContext.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import {createContext} from 'react';
-
-type CustomStatusBarContextType = {
- isRootStatusBarDisabled: boolean;
- disableRootStatusBar: (isDisabled: boolean) => void;
-};
-
-const CustomStatusBarContext = createContext({isRootStatusBarDisabled: false, disableRootStatusBar: () => undefined});
-
-export default CustomStatusBarContext;
-export {type CustomStatusBarContextType};
diff --git a/src/components/CustomStatusBar/CustomStatusBarContextProvider.tsx b/src/components/CustomStatusBar/CustomStatusBarContextProvider.tsx
deleted file mode 100644
index 27a5cac5d8cc..000000000000
--- a/src/components/CustomStatusBar/CustomStatusBarContextProvider.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import React, {useMemo, useState} from 'react';
-import CustomStatusBarContext from './CustomStatusBarContext';
-
-function CustomStatusBarContextProvider({children}: React.PropsWithChildren) {
- const [isRootStatusBarDisabled, disableRootStatusBar] = useState(false);
- const value = useMemo(
- () => ({
- isRootStatusBarDisabled,
- disableRootStatusBar,
- }),
- [isRootStatusBarDisabled],
- );
-
- return {children};
-}
-
-export default CustomStatusBarContextProvider;
diff --git a/src/components/CustomStatusBar/index.tsx b/src/components/CustomStatusBar/index.tsx
deleted file mode 100644
index 3b5022c60898..000000000000
--- a/src/components/CustomStatusBar/index.tsx
+++ /dev/null
@@ -1,91 +0,0 @@
-import {EventListenerCallback, NavigationContainerEventMap} from '@react-navigation/native';
-import PropTypes from 'prop-types';
-import React, {useCallback, useContext, useEffect} from 'react';
-import {navigationRef} from '@libs/Navigation/Navigation';
-import StatusBar from '@libs/StatusBar';
-import useTheme from '@styles/themes/useTheme';
-import CustomStatusBarContext from './CustomStatusBarContext';
-import updateStatusBarAppearance from './updateStatusBarAppearance';
-
-type CustomStatusBarProps = {
- isNested: boolean;
-};
-
-const propTypes = {
- /** Whether the CustomStatusBar is nested within another CustomStatusBar.
- * A nested CustomStatusBar will disable the "root" CustomStatusBar. */
- isNested: PropTypes.bool,
-};
-
-type CustomStatusBarType = {
- (props: CustomStatusBarProps): React.ReactNode;
- displayName: string;
- propTypes: typeof propTypes;
-};
-
-// eslint-disable-next-line react/function-component-definition
-const CustomStatusBar: CustomStatusBarType = ({isNested = false}) => {
- const {isRootStatusBarDisabled, disableRootStatusBar} = useContext(CustomStatusBarContext);
- const theme = useTheme();
-
- const isDisabled = !isNested && isRootStatusBarDisabled;
-
- useEffect(() => {
- if (isNested) {
- disableRootStatusBar(true);
- }
-
- return () => {
- if (!isNested) {
- return;
- }
- disableRootStatusBar(false);
- };
- }, [disableRootStatusBar, isNested]);
-
- const updateStatusBarStyle = useCallback>(() => {
- if (isDisabled) {
- return;
- }
-
- // Set the status bar colour depending on the current route.
- // If we don't have any colour defined for a route, fall back to
- // appBG color.
- const currentRoute = navigationRef.getCurrentRoute();
-
- let currentScreenBackgroundColor = theme.appBG;
- let statusBarStyle = theme.statusBarStyle;
- if (currentRoute && 'name' in currentRoute && currentRoute.name in theme.PAGE_THEMES) {
- const screenTheme = theme.PAGE_THEMES[currentRoute.name];
- currentScreenBackgroundColor = screenTheme.backgroundColor;
- statusBarStyle = screenTheme.statusBarStyle;
- }
-
- updateStatusBarAppearance({backgroundColor: currentScreenBackgroundColor, statusBarStyle});
- }, [isDisabled, theme.PAGE_THEMES, theme.appBG, theme.statusBarStyle]);
-
- useEffect(() => {
- navigationRef.addListener('state', updateStatusBarStyle);
-
- return () => navigationRef.removeListener('state', updateStatusBarStyle);
- }, [updateStatusBarStyle]);
-
- useEffect(() => {
- if (isDisabled) {
- return;
- }
-
- updateStatusBarAppearance({statusBarStyle: theme.statusBarStyle});
- }, [isDisabled, theme.statusBarStyle]);
-
- if (isDisabled) {
- return null;
- }
-
- return ;
-};
-
-CustomStatusBar.displayName = 'CustomStatusBar';
-CustomStatusBar.propTypes = propTypes;
-
-export default CustomStatusBar;
diff --git a/src/components/CustomStatusBarAndBackground/CustomStatusBarAndBackgroundContext.tsx b/src/components/CustomStatusBarAndBackground/CustomStatusBarAndBackgroundContext.tsx
new file mode 100644
index 000000000000..4a1a1cd2f964
--- /dev/null
+++ b/src/components/CustomStatusBarAndBackground/CustomStatusBarAndBackgroundContext.tsx
@@ -0,0 +1,11 @@
+import {createContext} from 'react';
+
+type CustomStatusBarAndBackgroundContextType = {
+ isRootStatusBarDisabled: boolean;
+ disableRootStatusBar: (isDisabled: boolean) => void;
+};
+
+const CustomStatusBarAndBackgroundContext = createContext({isRootStatusBarDisabled: false, disableRootStatusBar: () => undefined});
+
+export default CustomStatusBarAndBackgroundContext;
+export {type CustomStatusBarAndBackgroundContextType};
diff --git a/src/components/CustomStatusBarAndBackground/CustomStatusBarAndBackgroundContextProvider.tsx b/src/components/CustomStatusBarAndBackground/CustomStatusBarAndBackgroundContextProvider.tsx
new file mode 100644
index 000000000000..b4d553b60d0f
--- /dev/null
+++ b/src/components/CustomStatusBarAndBackground/CustomStatusBarAndBackgroundContextProvider.tsx
@@ -0,0 +1,17 @@
+import React, {useMemo, useState} from 'react';
+import CustomStatusBarAndBackgroundContext from './CustomStatusBarAndBackgroundContext';
+
+function CustomStatusBarAndBackgroundContextProvider({children}: React.PropsWithChildren) {
+ const [isRootStatusBarDisabled, disableRootStatusBar] = useState(false);
+ const value = useMemo(
+ () => ({
+ isRootStatusBarDisabled,
+ disableRootStatusBar,
+ }),
+ [isRootStatusBarDisabled],
+ );
+
+ return {children};
+}
+
+export default CustomStatusBarAndBackgroundContextProvider;
diff --git a/src/components/CustomStatusBarAndBackground/index.tsx b/src/components/CustomStatusBarAndBackground/index.tsx
new file mode 100644
index 000000000000..b84f9c6a6630
--- /dev/null
+++ b/src/components/CustomStatusBarAndBackground/index.tsx
@@ -0,0 +1,113 @@
+import React, {useCallback, useContext, useEffect, useRef, useState} from 'react';
+import useTheme from '@hooks/useTheme';
+import {navigationRef} from '@libs/Navigation/Navigation';
+import StatusBar from '@libs/StatusBar';
+import CustomStatusBarAndBackgroundContext from './CustomStatusBarAndBackgroundContext';
+import updateGlobalBackgroundColor from './updateGlobalBackgroundColor';
+import updateStatusBarAppearance from './updateStatusBarAppearance';
+
+type CustomStatusBarAndBackgroundProps = {
+ /** Whether the CustomStatusBar is nested within another CustomStatusBar.
+ * A nested CustomStatusBar will disable the "root" CustomStatusBar. */
+ isNested: boolean;
+};
+
+function CustomStatusBarAndBackground({isNested = false}: CustomStatusBarAndBackgroundProps) {
+ const {isRootStatusBarDisabled, disableRootStatusBar} = useContext(CustomStatusBarAndBackgroundContext);
+ const theme = useTheme();
+ const [statusBarStyle, setStatusBarStyle] = useState(theme.statusBarStyle);
+
+ const isDisabled = !isNested && isRootStatusBarDisabled;
+
+ // Disable the root status bar when a nested status bar is rendered
+ useEffect(() => {
+ if (isNested) {
+ disableRootStatusBar(true);
+ }
+
+ return () => {
+ if (!isNested) {
+ return;
+ }
+ disableRootStatusBar(false);
+ };
+ }, [disableRootStatusBar, isNested]);
+
+ const listenerCount = useRef(0);
+ const updateStatusBarStyle = useCallback(
+ (listenerId?: number) => {
+ // Check if this function is either called through the current navigation listener or the general useEffect which listens for theme changes.
+ if (listenerId !== undefined && listenerId !== listenerCount.current) {
+ return;
+ }
+
+ // Set the status bar colour depending on the current route.
+ // If we don't have any colour defined for a route, fall back to
+ // appBG color.
+ let currentRoute: ReturnType | undefined;
+ if (navigationRef.isReady()) {
+ currentRoute = navigationRef.getCurrentRoute();
+ }
+
+ let currentScreenBackgroundColor = theme.appBG;
+ let newStatusBarStyle = theme.statusBarStyle;
+ if (currentRoute && 'name' in currentRoute && currentRoute.name in theme.PAGE_THEMES) {
+ const screenTheme = theme.PAGE_THEMES[currentRoute.name];
+ currentScreenBackgroundColor = screenTheme.backgroundColor;
+ newStatusBarStyle = screenTheme.statusBarStyle;
+ }
+
+ // Don't update the status bar style if it's the same as the current one, to prevent flashing.
+ if (newStatusBarStyle === statusBarStyle) {
+ updateStatusBarAppearance({backgroundColor: currentScreenBackgroundColor});
+ } else {
+ updateStatusBarAppearance({backgroundColor: currentScreenBackgroundColor, statusBarStyle: newStatusBarStyle});
+ setStatusBarStyle(newStatusBarStyle);
+ }
+ },
+ [statusBarStyle, theme.PAGE_THEMES, theme.appBG, theme.statusBarStyle],
+ );
+
+ // Add navigation state listeners to update the status bar every time the route changes
+ // We have to pass a count as the listener id, because "react-navigation" somehow doesn't remove listeners properyl
+ useEffect(() => {
+ if (isDisabled) {
+ return;
+ }
+
+ const listenerId = ++listenerCount.current;
+ const listener = () => updateStatusBarStyle(listenerId);
+
+ navigationRef.addListener('state', listener);
+ return () => navigationRef.removeListener('state', listener);
+ }, [isDisabled, theme.appBG, updateStatusBarStyle]);
+
+ // Update the status bar style everytime the theme changes
+ useEffect(() => {
+ if (isDisabled) {
+ return;
+ }
+
+ updateStatusBarStyle();
+ }, [isDisabled, theme, updateStatusBarStyle]);
+
+ // Update the global background (on web) everytime the theme changes.
+ // The background of the html element needs to be updated, otherwise you will see a big contrast when resizing the window or when the keyboard is open on iOS web.
+ useEffect(() => {
+ if (isDisabled) {
+ return;
+ }
+
+ updateGlobalBackgroundColor(theme);
+ }, [isDisabled, theme]);
+
+ if (isDisabled) {
+ return null;
+ }
+
+ return ;
+}
+
+CustomStatusBarAndBackground.displayName = 'CustomStatusBarAndBackground';
+
+export default CustomStatusBarAndBackground;
diff --git a/src/components/CustomStatusBarAndBackground/updateGlobalBackgroundColor/index.ts b/src/components/CustomStatusBarAndBackground/updateGlobalBackgroundColor/index.ts
new file mode 100644
index 000000000000..dac994ba8597
--- /dev/null
+++ b/src/components/CustomStatusBarAndBackground/updateGlobalBackgroundColor/index.ts
@@ -0,0 +1,5 @@
+import type UpdateGlobalBackgroundColor from './types';
+
+const updateGlobalBackgroundColor: UpdateGlobalBackgroundColor = () => undefined;
+
+export default updateGlobalBackgroundColor;
diff --git a/src/components/CustomStatusBarAndBackground/updateGlobalBackgroundColor/index.website.ts b/src/components/CustomStatusBarAndBackground/updateGlobalBackgroundColor/index.website.ts
new file mode 100644
index 000000000000..481d866dbe4f
--- /dev/null
+++ b/src/components/CustomStatusBarAndBackground/updateGlobalBackgroundColor/index.website.ts
@@ -0,0 +1,8 @@
+import UpdateGlobalBackgroundColor from './types';
+
+const updateGlobalBackgroundColor: UpdateGlobalBackgroundColor = (theme) => {
+ const htmlElement = document.getElementsByTagName('html')[0];
+ htmlElement.style.setProperty('background-color', theme.appBG);
+};
+
+export default updateGlobalBackgroundColor;
diff --git a/src/components/CustomStatusBarAndBackground/updateGlobalBackgroundColor/types.ts b/src/components/CustomStatusBarAndBackground/updateGlobalBackgroundColor/types.ts
new file mode 100644
index 000000000000..83bd36a9428a
--- /dev/null
+++ b/src/components/CustomStatusBarAndBackground/updateGlobalBackgroundColor/types.ts
@@ -0,0 +1,5 @@
+import {ThemeColors} from '@styles/theme/types';
+
+type UpdateGlobalBackgroundColor = (theme: ThemeColors) => void;
+
+export default UpdateGlobalBackgroundColor;
diff --git a/src/components/CustomStatusBar/updateStatusBarAppearance/index.android.ts b/src/components/CustomStatusBarAndBackground/updateStatusBarAppearance/index.android.ts
similarity index 100%
rename from src/components/CustomStatusBar/updateStatusBarAppearance/index.android.ts
rename to src/components/CustomStatusBarAndBackground/updateStatusBarAppearance/index.android.ts
diff --git a/src/components/CustomStatusBar/updateStatusBarAppearance/index.ios.ts b/src/components/CustomStatusBarAndBackground/updateStatusBarAppearance/index.ios.ts
similarity index 100%
rename from src/components/CustomStatusBar/updateStatusBarAppearance/index.ios.ts
rename to src/components/CustomStatusBarAndBackground/updateStatusBarAppearance/index.ios.ts
diff --git a/src/components/CustomStatusBar/updateStatusBarAppearance/index.ts b/src/components/CustomStatusBarAndBackground/updateStatusBarAppearance/index.ts
similarity index 100%
rename from src/components/CustomStatusBar/updateStatusBarAppearance/index.ts
rename to src/components/CustomStatusBarAndBackground/updateStatusBarAppearance/index.ts
diff --git a/src/components/CustomStatusBar/updateStatusBarAppearance/types.ts b/src/components/CustomStatusBarAndBackground/updateStatusBarAppearance/types.ts
similarity index 77%
rename from src/components/CustomStatusBar/updateStatusBarAppearance/types.ts
rename to src/components/CustomStatusBarAndBackground/updateStatusBarAppearance/types.ts
index 3d16b5944a31..823f0059eccf 100644
--- a/src/components/CustomStatusBar/updateStatusBarAppearance/types.ts
+++ b/src/components/CustomStatusBarAndBackground/updateStatusBarAppearance/types.ts
@@ -1,4 +1,4 @@
-import {StatusBarStyle} from '@styles/styles';
+import {StatusBarStyle} from '@styles/index';
type UpdateStatusBarAppearanceProps = {
backgroundColor?: string;
diff --git a/src/components/DatePicker/CalendarPicker/ArrowIcon.js b/src/components/DatePicker/CalendarPicker/ArrowIcon.js
index a03e18085706..793aca9e7635 100644
--- a/src/components/DatePicker/CalendarPicker/ArrowIcon.js
+++ b/src/components/DatePicker/CalendarPicker/ArrowIcon.js
@@ -3,8 +3,8 @@ import React from 'react';
import {View} from 'react-native';
import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
const propTypes = {
diff --git a/src/components/DatePicker/CalendarPicker/YearPickerModal.js b/src/components/DatePicker/CalendarPicker/YearPickerModal.js
index f51d869efbac..e8883648051c 100644
--- a/src/components/DatePicker/CalendarPicker/YearPickerModal.js
+++ b/src/components/DatePicker/CalendarPicker/YearPickerModal.js
@@ -7,7 +7,7 @@ import ScreenWrapper from '@components/ScreenWrapper';
import SelectionList from '@components/SelectionList';
import {radioListItemPropTypes} from '@components/SelectionList/selectionListPropTypes';
import useLocalize from '@hooks/useLocalize';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
const propTypes = {
diff --git a/src/components/DatePicker/index.js b/src/components/DatePicker/index.js
index ac6454d25975..8af550c9dc66 100644
--- a/src/components/DatePicker/index.js
+++ b/src/components/DatePicker/index.js
@@ -8,7 +8,7 @@ import * as Expensicons from '@components/Icon/Expensicons';
import TextInput from '@components/TextInput';
import {propTypes as baseTextInputPropTypes, defaultProps as defaultBaseTextInputPropTypes} from '@components/TextInput/BaseTextInput/baseTextInputPropTypes';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
import CalendarPicker from './CalendarPicker';
diff --git a/src/components/DeeplinkWrapper/DeeplinkRedirectLoadingIndicator.js b/src/components/DeeplinkWrapper/DeeplinkRedirectLoadingIndicator.tsx
similarity index 64%
rename from src/components/DeeplinkWrapper/DeeplinkRedirectLoadingIndicator.js
rename to src/components/DeeplinkWrapper/DeeplinkRedirectLoadingIndicator.tsx
index 3c7366949ac1..c404ff5fa71f 100644
--- a/src/components/DeeplinkWrapper/DeeplinkRedirectLoadingIndicator.js
+++ b/src/components/DeeplinkWrapper/DeeplinkRedirectLoadingIndicator.tsx
@@ -1,40 +1,34 @@
-import PropTypes from 'prop-types';
import React from 'react';
import {View} from 'react-native';
-import {withOnyx} from 'react-native-onyx';
+import {OnyxEntry, withOnyx} from 'react-native-onyx';
import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import * as Illustrations from '@components/Icon/Illustrations';
import Text from '@components/Text';
import TextLink from '@components/TextLink';
-import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
-import compose from '@libs/compose';
+import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
+import * as OnyxTypes from '@src/types/onyx';
-const propTypes = {
- openLinkInBrowser: PropTypes.func.isRequired,
-
- session: PropTypes.shape({
- /** Currently logged-in user email */
- email: PropTypes.string,
- }),
-
- ...withLocalizePropTypes,
+type DeeplinkRedirectLoadingIndicatorOnyxProps = {
+ /** Current user session */
+ session: OnyxEntry;
};
-const defaultProps = {
- session: {
- email: '',
- },
+type DeeplinkRedirectLoadingIndicatorProps = DeeplinkRedirectLoadingIndicatorOnyxProps & {
+ /** Opens the link in the browser */
+ openLinkInBrowser: (value: boolean) => void;
};
-function DeeplinkRedirectLoadingIndicator({translate, openLinkInBrowser, session}) {
+function DeeplinkRedirectLoadingIndicator({openLinkInBrowser, session}: DeeplinkRedirectLoadingIndicatorProps) {
+ const {translate} = useLocalize();
const theme = useTheme();
const styles = useThemeStyles();
+
return (
@@ -46,8 +40,8 @@ function DeeplinkRedirectLoadingIndicator({translate, openLinkInBrowser, session
/>
{translate('deeplinkWrapper.launching')}
-
- {translate('deeplinkWrapper.loggedInAs', {email: session.email})}
+
+ {translate('deeplinkWrapper.loggedInAs', {email: session?.email ?? ''})}
{translate('deeplinkWrapper.doNotSeePrompt')} openLinkInBrowser(true)}>{translate('deeplinkWrapper.tryAgain')}
{translate('deeplinkWrapper.or')} Navigation.navigate(ROUTES.HOME)}>{translate('deeplinkWrapper.continueInWeb')}.
@@ -66,15 +60,10 @@ function DeeplinkRedirectLoadingIndicator({translate, openLinkInBrowser, session
);
}
-DeeplinkRedirectLoadingIndicator.propTypes = propTypes;
-DeeplinkRedirectLoadingIndicator.defaultProps = defaultProps;
DeeplinkRedirectLoadingIndicator.displayName = 'DeeplinkRedirectLoadingIndicator';
-export default compose(
- withLocalize,
- withOnyx({
- session: {
- key: ONYXKEYS.SESSION,
- },
- }),
-)(DeeplinkRedirectLoadingIndicator);
+export default withOnyx({
+ session: {
+ key: ONYXKEYS.SESSION,
+ },
+})(DeeplinkRedirectLoadingIndicator);
diff --git a/src/components/DeeplinkWrapper/index.js b/src/components/DeeplinkWrapper/index.js
deleted file mode 100644
index de50d9bdf134..000000000000
--- a/src/components/DeeplinkWrapper/index.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import PropTypes from 'prop-types';
-
-const propTypes = {
- /** Children to render. */
- children: PropTypes.node.isRequired,
-};
-
-function DeeplinkWrapper({children}) {
- return children;
-}
-
-DeeplinkWrapper.propTypes = propTypes;
-
-export default DeeplinkWrapper;
diff --git a/src/components/DeeplinkWrapper/index.tsx b/src/components/DeeplinkWrapper/index.tsx
new file mode 100644
index 000000000000..4b0382bd6b14
--- /dev/null
+++ b/src/components/DeeplinkWrapper/index.tsx
@@ -0,0 +1,9 @@
+import DeeplinkWrapperProps from './types';
+
+function DeeplinkWrapper({children}: DeeplinkWrapperProps) {
+ return children;
+}
+
+DeeplinkWrapper.displayName = 'DeeplinkWrapper';
+
+export default DeeplinkWrapper;
diff --git a/src/components/DeeplinkWrapper/index.website.js b/src/components/DeeplinkWrapper/index.website.tsx
similarity index 79%
rename from src/components/DeeplinkWrapper/index.website.js
rename to src/components/DeeplinkWrapper/index.website.tsx
index d81c99657dd8..2cae91e2f2a0 100644
--- a/src/components/DeeplinkWrapper/index.website.js
+++ b/src/components/DeeplinkWrapper/index.website.tsx
@@ -1,7 +1,5 @@
import Str from 'expensify-common/lib/str';
-import PropTypes from 'prop-types';
import {useEffect, useRef, useState} from 'react';
-import _ from 'underscore';
import * as Browser from '@libs/Browser';
import Navigation from '@libs/Navigation/Navigation';
import navigationRef from '@libs/Navigation/navigationRef';
@@ -10,17 +8,9 @@ import * as App from '@userActions/App';
import CONFIG from '@src/CONFIG';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
+import DeeplinkWrapperProps from './types';
-const propTypes = {
- /** Children to render. */
- children: PropTypes.node.isRequired,
- /** User authentication status */
- isAuthenticated: PropTypes.bool.isRequired,
- /** The auto authentication status */
- autoAuthState: PropTypes.string,
-};
-
-function isMacOSWeb() {
+function isMacOSWeb(): boolean {
return !Browser.isMobile() && typeof navigator === 'object' && typeof navigator.userAgent === 'string' && /Mac/i.test(navigator.userAgent) && !/Electron/i.test(navigator.userAgent);
}
@@ -38,10 +28,11 @@ function promptToOpenInDesktopApp() {
App.beginDeepLinkRedirect(!isMagicLink);
}
}
-function DeeplinkWrapper({children, isAuthenticated, autoAuthState}) {
- const [currentScreen, setCurrentScreen] = useState();
+
+function DeeplinkWrapper({children, isAuthenticated, autoAuthState}: DeeplinkWrapperProps) {
+ const [currentScreen, setCurrentScreen] = useState();
const [hasShownPrompt, setHasShownPrompt] = useState(false);
- const removeListener = useRef();
+ const removeListener = useRef<() => void>();
useEffect(() => {
// If we've shown the prompt and still have a listener registered,
@@ -55,21 +46,21 @@ function DeeplinkWrapper({children, isAuthenticated, autoAuthState}) {
setHasShownPrompt(false);
Navigation.isNavigationReady().then(() => {
// Get initial route
- const initialRoute = navigationRef.current.getCurrentRoute();
- setCurrentScreen(initialRoute.name);
+ const initialRoute = navigationRef.current?.getCurrentRoute();
+ setCurrentScreen(initialRoute?.name);
- removeListener.current = navigationRef.current.addListener('state', (event) => {
+ removeListener.current = navigationRef.current?.addListener('state', (event) => {
setCurrentScreen(Navigation.getRouteNameFromStateEvent(event));
});
});
}
}, [hasShownPrompt, isAuthenticated]);
+
useEffect(() => {
// According to the design, we don't support unlink in Desktop app https://github.com/Expensify/App/issues/19681#issuecomment-1610353099
- const isUnsupportedDeeplinkRoute = _.some([CONST.REGEX.ROUTES.UNLINK_LOGIN], (unsupportRouteRegex) => {
- const routeRegex = new RegExp(unsupportRouteRegex);
- return routeRegex.test(window.location.pathname);
- });
+ const routeRegex = new RegExp(CONST.REGEX.ROUTES.UNLINK_LOGIN);
+ const isUnsupportedDeeplinkRoute = routeRegex.test(window.location.pathname);
+
// Making a few checks to exit early before checking authentication status
if (!isMacOSWeb() || isUnsupportedDeeplinkRoute || hasShownPrompt || CONFIG.ENVIRONMENT === CONST.ENVIRONMENT.DEV || autoAuthState === CONST.AUTO_AUTH_STATE.NOT_STARTED) {
return;
@@ -99,5 +90,6 @@ function DeeplinkWrapper({children, isAuthenticated, autoAuthState}) {
return children;
}
-DeeplinkWrapper.propTypes = propTypes;
+DeeplinkWrapper.displayName = 'DeeplinkWrapper';
+
export default DeeplinkWrapper;
diff --git a/src/components/DeeplinkWrapper/types.ts b/src/components/DeeplinkWrapper/types.ts
new file mode 100644
index 000000000000..dfd56b62573d
--- /dev/null
+++ b/src/components/DeeplinkWrapper/types.ts
@@ -0,0 +1,11 @@
+import ChildrenProps from '@src/types/utils/ChildrenProps';
+
+type DeeplinkWrapperProps = ChildrenProps & {
+ /** User authentication status */
+ isAuthenticated: boolean;
+
+ /** The auto authentication status */
+ autoAuthState?: string;
+};
+
+export default DeeplinkWrapperProps;
diff --git a/src/components/DisplayNames/DisplayNamesTooltipItem.tsx b/src/components/DisplayNames/DisplayNamesTooltipItem.tsx
index 82f9c5799b78..440457d22965 100644
--- a/src/components/DisplayNames/DisplayNamesTooltipItem.tsx
+++ b/src/components/DisplayNames/DisplayNamesTooltipItem.tsx
@@ -2,8 +2,8 @@ import React, {RefObject, useCallback} from 'react';
import {Text as RNText, StyleProp, TextStyle} from 'react-native';
import Text from '@components/Text';
import UserDetailsTooltip from '@components/UserDetailsTooltip';
+import useThemeStyles from '@hooks/useThemeStyles';
import {AvatarSource} from '@libs/UserUtils';
-import useThemeStyles from '@styles/useThemeStyles';
type DisplayNamesTooltipItemProps = {
index?: number;
diff --git a/src/components/DisplayNames/DisplayNamesWithTooltip.tsx b/src/components/DisplayNames/DisplayNamesWithTooltip.tsx
index 8c8720c7c99f..43061ada9a94 100644
--- a/src/components/DisplayNames/DisplayNamesWithTooltip.tsx
+++ b/src/components/DisplayNames/DisplayNamesWithTooltip.tsx
@@ -2,7 +2,7 @@ import React, {Fragment, useCallback, useRef} from 'react';
import {Text as RNText, View} from 'react-native';
import Text from '@components/Text';
import Tooltip from '@components/Tooltip';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import DisplayNamesTooltipItem from './DisplayNamesTooltipItem';
import DisplayNamesProps from './types';
diff --git a/src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx b/src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx
index 1854ebe2353d..761b0b66ee2c 100644
--- a/src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx
+++ b/src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import {StyleProp, TextStyle} from 'react-native';
import Text from '@components/Text';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
type DisplayNamesWithoutTooltipProps = {
/** The full title of the DisplayNames component (not split up) */
diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js
index cbf25fd2753b..7d1d3c99f48d 100644
--- a/src/components/DistanceEReceipt.js
+++ b/src/components/DistanceEReceipt.js
@@ -5,13 +5,13 @@ import _ from 'underscore';
import EReceiptBackground from '@assets/images/eReceipt_background.svg';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as CurrencyUtils from '@libs/CurrencyUtils';
import * as ReceiptUtils from '@libs/ReceiptUtils';
import * as ReportUtils from '@libs/ReportUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
import tryResolveUrlFromApiRoot from '@libs/tryResolveUrlFromApiRoot';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import Icon from './Icon';
import * as Expensicons from './Icon/Expensicons';
import PendingMapView from './MapView/PendingMapView';
diff --git a/src/components/DistanceMapView/index.android.js b/src/components/DistanceMapView/index.android.js
index 532d42ac0be5..fa40bd50673e 100644
--- a/src/components/DistanceMapView/index.android.js
+++ b/src/components/DistanceMapView/index.android.js
@@ -6,8 +6,8 @@ import * as Expensicons from '@components/Icon/Expensicons';
import MapView from '@components/MapView';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as distanceMapViewPropTypes from './distanceMapViewPropTypes';
function DistanceMapView(props) {
diff --git a/src/components/DistanceRequest/DistanceRequestFooter.js b/src/components/DistanceRequest/DistanceRequestFooter.js
index d374f90a1b6c..d2813c152a64 100644
--- a/src/components/DistanceRequest/DistanceRequestFooter.js
+++ b/src/components/DistanceRequest/DistanceRequestFooter.js
@@ -10,9 +10,9 @@ import DistanceMapView from '@components/DistanceMapView';
import * as Expensicons from '@components/Icon/Expensicons';
import transactionPropTypes from '@components/transactionPropTypes';
import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as TransactionUtils from '@libs/TransactionUtils';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/components/DistanceRequest/DistanceRequestRenderItem.js b/src/components/DistanceRequest/DistanceRequestRenderItem.js
index 1735e244a347..2aa2ac58f379 100644
--- a/src/components/DistanceRequest/DistanceRequestRenderItem.js
+++ b/src/components/DistanceRequest/DistanceRequestRenderItem.js
@@ -5,7 +5,7 @@ import _ from 'underscore';
import * as Expensicons from '@components/Icon/Expensicons';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import useLocalize from '@hooks/useLocalize';
-import useTheme from '@styles/themes/useTheme';
+import useTheme from '@hooks/useTheme';
const propTypes = {
/** The waypoints for the distance request */
diff --git a/src/components/DistanceRequest/index.js b/src/components/DistanceRequest/index.js
index be34a42ead5e..b2fd16cb7d97 100644
--- a/src/components/DistanceRequest/index.js
+++ b/src/components/DistanceRequest/index.js
@@ -14,12 +14,12 @@ import transactionPropTypes from '@components/transactionPropTypes';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import usePrevious from '@hooks/usePrevious';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as ErrorUtils from '@libs/ErrorUtils';
import * as IOUUtils from '@libs/IOUUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as TransactionUtils from '@libs/TransactionUtils';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import * as MapboxToken from '@userActions/MapboxToken';
import * as Transaction from '@userActions/Transaction';
diff --git a/src/components/DotIndicatorMessage.tsx b/src/components/DotIndicatorMessage.tsx
index 6a7d78768ed7..65afe8c7e4eb 100644
--- a/src/components/DotIndicatorMessage.tsx
+++ b/src/components/DotIndicatorMessage.tsx
@@ -1,11 +1,11 @@
/* eslint-disable react/no-array-index-key */
import React from 'react';
import {StyleProp, TextStyle, View, ViewStyle} from 'react-native';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import fileDownload from '@libs/fileDownload';
import * as Localize from '@libs/Localize';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import Icon from './Icon';
import * as Expensicons from './Icon/Expensicons';
diff --git a/src/components/DragAndDrop/NoDropZone/index.tsx b/src/components/DragAndDrop/NoDropZone/index.tsx
index 9f2c700b8918..4760a16fd20b 100644
--- a/src/components/DragAndDrop/NoDropZone/index.tsx
+++ b/src/components/DragAndDrop/NoDropZone/index.tsx
@@ -1,7 +1,7 @@
import React, {useRef} from 'react';
import {View} from 'react-native';
import useDragAndDrop from '@hooks/useDragAndDrop';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import type NoDropZoneProps from './types';
function NoDropZone({children}: NoDropZoneProps) {
diff --git a/src/components/DragAndDrop/Provider/index.tsx b/src/components/DragAndDrop/Provider/index.tsx
index 761c512497ac..a5da9cc45a36 100644
--- a/src/components/DragAndDrop/Provider/index.tsx
+++ b/src/components/DragAndDrop/Provider/index.tsx
@@ -3,7 +3,7 @@ import Str from 'expensify-common/lib/str';
import React, {useCallback, useEffect, useMemo, useRef} from 'react';
import {View} from 'react-native';
import useDragAndDrop from '@hooks/useDragAndDrop';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import type {DragAndDropContextParams, DragAndDropProviderProps, SetOnDropHandlerCallback} from './types';
const DragAndDropContext = React.createContext({});
diff --git a/src/components/DraggableList/index.native.tsx b/src/components/DraggableList/index.native.tsx
index e7ff058234b7..f532b21720da 100644
--- a/src/components/DraggableList/index.native.tsx
+++ b/src/components/DraggableList/index.native.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import DraggableFlatList from 'react-native-draggable-flatlist';
import {FlatList} from 'react-native-gesture-handler';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import type {DraggableListProps} from './types';
function DraggableList({renderClone, shouldUsePortal, ...viewProps}: DraggableListProps, ref: React.ForwardedRef>) {
diff --git a/src/components/DraggableList/index.tsx b/src/components/DraggableList/index.tsx
index 28128002c589..b92691075424 100644
--- a/src/components/DraggableList/index.tsx
+++ b/src/components/DraggableList/index.tsx
@@ -1,7 +1,7 @@
import React, {useCallback} from 'react';
import {DragDropContext, Draggable, Droppable, type OnDragEndResponder} from 'react-beautiful-dnd';
import {ScrollView} from 'react-native';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import type {DraggableListProps} from './types';
import useDraggableInPortal from './useDraggableInPortal';
diff --git a/src/components/EReceipt.js b/src/components/EReceipt.js
index f5e5b7f2f6b3..12cea0df04ac 100644
--- a/src/components/EReceipt.js
+++ b/src/components/EReceipt.js
@@ -3,11 +3,11 @@ import React from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import useLocalize from '@hooks/useLocalize';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as CardUtils from '@libs/CardUtils';
import * as CurrencyUtils from '@libs/CurrencyUtils';
import * as ReportUtils from '@libs/ReportUtils';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/components/EReceiptThumbnail.js b/src/components/EReceiptThumbnail.js
index 1f719862412b..7c782a0aa327 100644
--- a/src/components/EReceiptThumbnail.js
+++ b/src/components/EReceiptThumbnail.js
@@ -2,9 +2,9 @@ import PropTypes from 'prop-types';
import React, {useMemo, useState} from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as ReportUtils from '@libs/ReportUtils';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/components/EmojiPicker/CategoryShortcutBar.js b/src/components/EmojiPicker/CategoryShortcutBar.js
index c0c9fb8ea161..facafea13a70 100644
--- a/src/components/EmojiPicker/CategoryShortcutBar.js
+++ b/src/components/EmojiPicker/CategoryShortcutBar.js
@@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
import React from 'react';
import {View} from 'react-native';
import _ from 'underscore';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import CategoryShortcutButton from './CategoryShortcutButton';
const propTypes = {
diff --git a/src/components/EmojiPicker/CategoryShortcutButton.js b/src/components/EmojiPicker/CategoryShortcutButton.js
index 1fcbe7e863e4..a7dc0d7bf564 100644
--- a/src/components/EmojiPicker/CategoryShortcutButton.js
+++ b/src/components/EmojiPicker/CategoryShortcutButton.js
@@ -4,10 +4,10 @@ import Icon from '@components/Icon';
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
import Tooltip from '@components/Tooltip';
import useLocalize from '@hooks/useLocalize';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import getButtonState from '@libs/getButtonState';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import CONST from '@src/CONST';
diff --git a/src/components/EmojiPicker/EmojiPicker.js b/src/components/EmojiPicker/EmojiPicker.js
index 96d7ee88b816..6c05a4b6a5b5 100644
--- a/src/components/EmojiPicker/EmojiPicker.js
+++ b/src/components/EmojiPicker/EmojiPicker.js
@@ -4,10 +4,10 @@ import {Dimensions} from 'react-native';
import _ from 'underscore';
import PopoverWithMeasuredContent from '@components/PopoverWithMeasuredContent';
import withViewportOffsetTop from '@components/withViewportOffsetTop';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import calculateAnchorPosition from '@libs/calculateAnchorPosition';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import EmojiPickerMenu from './EmojiPickerMenu';
diff --git a/src/components/EmojiPicker/EmojiPickerButton.js b/src/components/EmojiPicker/EmojiPickerButton.js
index 165646d4795d..869fe1edbfe5 100644
--- a/src/components/EmojiPicker/EmojiPickerButton.js
+++ b/src/components/EmojiPicker/EmojiPickerButton.js
@@ -5,9 +5,9 @@ import * as Expensicons from '@components/Icon/Expensicons';
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
import Tooltip from '@components/Tooltip/PopoverAnchorTooltip';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import getButtonState from '@libs/getButtonState';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import * as EmojiPickerAction from '@userActions/EmojiPickerAction';
const propTypes = {
diff --git a/src/components/EmojiPicker/EmojiPickerButtonDropdown.js b/src/components/EmojiPicker/EmojiPickerButtonDropdown.js
index 8135fa38e992..43687ec4b7a8 100644
--- a/src/components/EmojiPicker/EmojiPickerButtonDropdown.js
+++ b/src/components/EmojiPicker/EmojiPickerButtonDropdown.js
@@ -5,11 +5,11 @@ import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
import Text from '@components/Text';
-import Tooltip from '@components/Tooltip';
+import Tooltip from '@components/Tooltip/PopoverAnchorTooltip';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import getButtonState from '@libs/getButtonState';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import * as EmojiPickerAction from '@userActions/EmojiPickerAction';
import CONST from '@src/CONST';
@@ -47,7 +47,7 @@ function EmojiPickerButtonDropdown(props) {
- debounce((comment, onExceededMaxCommentLength) => {
- const newCommentLength = ReportUtils.getCommentLength(comment);
- setCommentLength(newCommentLength);
- onExceededMaxCommentLength(newCommentLength > CONST.MAX_COMMENT_LENGTH);
- }, CONST.TIMING.COMMENT_LENGTH_DEBOUNCE_TIME),
- [],
- );
-
- useEffect(() => {
- updateCommentLength(props.comment, props.onExceededMaxCommentLength);
- }, [props.comment, props.onExceededMaxCommentLength, updateCommentLength]);
- if (commentLength <= CONST.MAX_COMMENT_LENGTH) {
+ if (!props.shouldShowError) {
return null;
}
-
return (
`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`,
- initialValue: '',
- },
-})(ExceededCommentLength);
+export default ExceededCommentLength;
diff --git a/src/components/ExpensifyWordmark.tsx b/src/components/ExpensifyWordmark.tsx
index 49559d1cc6d5..b1ef7c093e29 100644
--- a/src/components/ExpensifyWordmark.tsx
+++ b/src/components/ExpensifyWordmark.tsx
@@ -5,9 +5,9 @@ import DevLogo from '@assets/images/expensify-logo--dev.svg';
import StagingLogo from '@assets/images/expensify-logo--staging.svg';
import ProductionLogo from '@assets/images/expensify-wordmark.svg';
import useEnvironment from '@hooks/useEnvironment';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
+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';
import withWindowDimensions from './withWindowDimensions';
diff --git a/src/components/FeatureList.js b/src/components/FeatureList.js
index 7b0c70372579..bf0852d179f1 100644
--- a/src/components/FeatureList.js
+++ b/src/components/FeatureList.js
@@ -3,7 +3,7 @@ import React from 'react';
import {View} from 'react-native';
import _ from 'underscore';
import useLocalize from '@hooks/useLocalize';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import MenuItem from './MenuItem';
import menuItemPropTypes from './menuItemPropTypes';
import Text from './Text';
diff --git a/src/components/FixedFooter.tsx b/src/components/FixedFooter.tsx
index 4b32e1b8ce81..7fd6811c1df6 100644
--- a/src/components/FixedFooter.tsx
+++ b/src/components/FixedFooter.tsx
@@ -1,6 +1,6 @@
import React, {ReactNode} from 'react';
import {StyleProp, View, ViewStyle} from 'react-native';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
type FixedFooterProps = {
/** Children to wrap in FixedFooter. */
diff --git a/src/components/FloatingActionButton.js b/src/components/FloatingActionButton.js
deleted file mode 100644
index 791eb150f8c9..000000000000
--- a/src/components/FloatingActionButton.js
+++ /dev/null
@@ -1,132 +0,0 @@
-import PropTypes from 'prop-types';
-import React, {PureComponent} from 'react';
-import {Animated, Easing, View} from 'react-native';
-import compose from '@libs/compose';
-import Icon from './Icon';
-import * as Expensicons from './Icon/Expensicons';
-import PressableWithFeedback from './Pressable/PressableWithFeedback';
-import Tooltip from './Tooltip/PopoverAnchorTooltip';
-import withLocalize, {withLocalizePropTypes} from './withLocalize';
-import withStyleUtils, {withStyleUtilsPropTypes} from './withStyleUtils';
-import withTheme, {withThemePropTypes} from './withTheme';
-import withThemeStyles, {withThemeStylesPropTypes} from './withThemeStyles';
-
-const AnimatedIcon = Animated.createAnimatedComponent(Icon);
-AnimatedIcon.displayName = 'AnimatedIcon';
-
-const AnimatedPressable = Animated.createAnimatedComponent(PressableWithFeedback);
-AnimatedPressable.displayName = 'AnimatedPressable';
-
-const propTypes = {
- // Callback to fire on request to toggle the FloatingActionButton
- onPress: PropTypes.func.isRequired,
-
- // Current state (active or not active) of the component
- isActive: PropTypes.bool.isRequired,
-
- // Ref for the button
- buttonRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
-
- ...withLocalizePropTypes,
- ...withThemePropTypes,
- ...withThemeStylesPropTypes,
- ...withStyleUtilsPropTypes,
-};
-
-const defaultProps = {
- buttonRef: () => {},
-};
-
-class FloatingActionButton extends PureComponent {
- constructor(props) {
- super(props);
- this.animatedValue = new Animated.Value(props.isActive ? 1 : 0);
- }
-
- componentDidUpdate(prevProps) {
- if (prevProps.isActive === this.props.isActive) {
- return;
- }
-
- this.animateFloatingActionButton();
- }
-
- /**
- * Animates the floating action button
- * Method is called when the isActive prop changes
- */
- animateFloatingActionButton() {
- const animationFinalValue = this.props.isActive ? 1 : 0;
-
- Animated.timing(this.animatedValue, {
- toValue: animationFinalValue,
- duration: 340,
- easing: Easing.inOut(Easing.ease),
- useNativeDriver: false,
- }).start();
- }
-
- render() {
- const rotate = this.animatedValue.interpolate({
- inputRange: [0, 1],
- outputRange: ['0deg', '135deg'],
- });
-
- const backgroundColor = this.animatedValue.interpolate({
- inputRange: [0, 1],
- outputRange: [this.props.theme.success, this.props.theme.buttonDefaultBG],
- });
-
- const fill = this.animatedValue.interpolate({
- inputRange: [0, 1],
- outputRange: [this.props.theme.textLight, this.props.theme.textDark],
- });
-
- return (
-
-
- {
- this.fabPressable = el;
- if (this.props.buttonRef) {
- this.props.buttonRef.current = el;
- }
- }}
- accessibilityLabel={this.props.accessibilityLabel}
- role={this.props.role}
- pressDimmingValue={1}
- onPress={(e) => {
- // Drop focus to avoid blue focus ring.
- this.fabPressable.blur();
- this.props.onPress(e);
- }}
- onLongPress={() => {}}
- style={[this.props.themeStyles.floatingActionButton, this.props.StyleUtils.getAnimatedFABStyle(rotate, backgroundColor)]}
- >
-
-
-
-
- );
- }
-}
-
-FloatingActionButton.propTypes = propTypes;
-FloatingActionButton.defaultProps = defaultProps;
-
-const FloatingActionButtonWithLocalize = withLocalize(FloatingActionButton);
-
-const FloatingActionButtonWithLocalizeWithRef = React.forwardRef((props, ref) => (
-
-));
-
-FloatingActionButtonWithLocalizeWithRef.displayName = 'FloatingActionButtonWithLocalizeWithRef';
-
-export default compose(withThemeStyles, withTheme, withStyleUtils)(FloatingActionButtonWithLocalizeWithRef);
diff --git a/src/components/FloatingActionButton/FabPlusIcon.js b/src/components/FloatingActionButton/FabPlusIcon.js
new file mode 100644
index 000000000000..09afa00f119d
--- /dev/null
+++ b/src/components/FloatingActionButton/FabPlusIcon.js
@@ -0,0 +1,49 @@
+import PropTypes from 'prop-types';
+import React, {useEffect} from 'react';
+import Animated, {Easing, interpolateColor, useAnimatedProps, useSharedValue, withTiming} from 'react-native-reanimated';
+import Svg, {Path} from 'react-native-svg';
+import useTheme from '@hooks/useTheme';
+
+const AnimatedPath = Animated.createAnimatedComponent(Path);
+
+const propTypes = {
+ /* Current state (active or not active) of the component */
+ isActive: PropTypes.bool.isRequired,
+};
+
+function FabPlusIcon({isActive}) {
+ const theme = useTheme();
+ const animatedValue = useSharedValue(isActive ? 1 : 0);
+
+ useEffect(() => {
+ animatedValue.value = withTiming(isActive ? 1 : 0, {
+ duration: 340,
+ easing: Easing.inOut(Easing.ease),
+ });
+ }, [isActive, animatedValue]);
+
+ const animatedProps = useAnimatedProps(() => {
+ const fill = interpolateColor(animatedValue.value, [0, 1], [theme.textLight, theme.textDark]);
+
+ return {
+ fill,
+ };
+ });
+
+ return (
+
+ );
+}
+
+FabPlusIcon.propTypes = propTypes;
+FabPlusIcon.displayName = 'FabPlusIcon';
+
+export default FabPlusIcon;
diff --git a/src/components/FloatingActionButton/index.js b/src/components/FloatingActionButton/index.js
new file mode 100644
index 000000000000..d341396c44b7
--- /dev/null
+++ b/src/components/FloatingActionButton/index.js
@@ -0,0 +1,85 @@
+import PropTypes from 'prop-types';
+import React, {useEffect, useRef} from 'react';
+import {View} from 'react-native';
+import Animated, {Easing, interpolateColor, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
+import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
+import Tooltip from '@components/Tooltip/PopoverAnchorTooltip';
+import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
+import FabPlusIcon from './FabPlusIcon';
+
+const AnimatedPressable = Animated.createAnimatedComponent(PressableWithFeedback);
+AnimatedPressable.displayName = 'AnimatedPressable';
+
+const propTypes = {
+ /* Callback to fire on request to toggle the FloatingActionButton */
+ onPress: PropTypes.func.isRequired,
+
+ /* Current state (active or not active) of the component */
+ isActive: PropTypes.bool.isRequired,
+
+ /* An accessibility label for the button */
+ accessibilityLabel: PropTypes.string.isRequired,
+
+ /* An accessibility role for the button */
+ role: PropTypes.string.isRequired,
+};
+
+const FloatingActionButton = React.forwardRef(({onPress, isActive, accessibilityLabel, role}, ref) => {
+ const theme = useTheme();
+ const styles = useThemeStyles();
+ const {translate} = useLocalize();
+ const fabPressable = useRef(null);
+ const animatedValue = useSharedValue(isActive ? 1 : 0);
+ const buttonRef = ref;
+
+ useEffect(() => {
+ animatedValue.value = withTiming(isActive ? 1 : 0, {
+ duration: 340,
+ easing: Easing.inOut(Easing.ease),
+ });
+ }, [isActive, animatedValue]);
+
+ const animatedStyle = useAnimatedStyle(() => {
+ const backgroundColor = interpolateColor(animatedValue.value, [0, 1], [theme.success, theme.buttonDefaultBG]);
+
+ return {
+ transform: [{rotate: `${animatedValue.value * 135}deg`}],
+ backgroundColor,
+ borderRadius: styles.floatingActionButton.borderRadius,
+ };
+ });
+
+ return (
+
+
+ {
+ fabPressable.current = el;
+ if (buttonRef) {
+ buttonRef.current = el;
+ }
+ }}
+ accessibilityLabel={accessibilityLabel}
+ role={role}
+ pressDimmingValue={1}
+ onPress={(e) => {
+ // Drop focus to avoid blue focus ring.
+ fabPressable.current.blur();
+ onPress(e);
+ }}
+ onLongPress={() => {}}
+ style={[styles.floatingActionButton, animatedStyle]}
+ >
+
+
+
+
+ );
+});
+
+FloatingActionButton.propTypes = propTypes;
+FloatingActionButton.displayName = 'FloatingActionButton';
+
+export default FloatingActionButton;
diff --git a/src/components/FocusModeNotification.js b/src/components/FocusModeNotification.js
index ea6db244091a..e846c1f188e2 100644
--- a/src/components/FocusModeNotification.js
+++ b/src/components/FocusModeNotification.js
@@ -1,7 +1,7 @@
import React, {useEffect} from 'react';
import useEnvironment from '@hooks/useEnvironment';
import useLocalize from '@hooks/useLocalize';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as Link from '@userActions/Link';
import * as User from '@userActions/User';
import CONST from '@src/CONST';
@@ -10,9 +10,9 @@ import Text from './Text';
import TextLinkWithRef from './TextLink';
function FocusModeNotification() {
+ const styles = useThemeStyles();
const {environmentURL} = useEnvironment();
const {translate} = useLocalize();
- const styles = useThemeStyles();
useEffect(() => {
User.updateChatPriorityMode(CONST.PRIORITY_MODE.GSD, true);
}, []);
diff --git a/src/components/Form.js b/src/components/Form.js
index ad5fcf611e9b..7b6f587e7bd1 100644
--- a/src/components/Form.js
+++ b/src/components/Form.js
@@ -1,16 +1,16 @@
import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
-import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
+import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react';
import {Keyboard, ScrollView, StyleSheet} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import * as ErrorUtils from '@libs/ErrorUtils';
import FormUtils from '@libs/FormUtils';
import * as ValidationUtils from '@libs/ValidationUtils';
import Visibility from '@libs/Visibility';
import stylePropTypes from '@styles/stylePropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as FormActions from '@userActions/FormActions';
import CONST from '@src/CONST';
import FormAlertWithSubmitButton from './FormAlertWithSubmitButton';
@@ -26,7 +26,7 @@ const propTypes = {
formID: PropTypes.string.isRequired,
/** Text to be displayed in the submit button */
- submitButtonText: PropTypes.string.isRequired,
+ submitButtonText: PropTypes.string,
/** Controls the submit button's visibility */
isSubmitButtonVisible: PropTypes.bool,
@@ -88,6 +88,9 @@ const propTypes = {
/** Information about the network */
network: networkPropTypes.isRequired,
+ /** Style for the error message for submit button */
+ errorMessageStyle: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]),
+
...withLocalizePropTypes,
};
@@ -104,11 +107,13 @@ const defaultProps = {
shouldValidateOnBlur: true,
footerContent: null,
style: [],
+ errorMessageStyle: [],
submitButtonStyles: [],
validate: () => ({}),
+ submitButtonText: '',
};
-function Form(props) {
+const Form = forwardRef((props, forwardedRef) => {
const styles = useThemeStyles();
const [errors, setErrors] = useState({});
const [inputValues, setInputValues] = useState(() => ({...props.draftValues}));
@@ -245,6 +250,30 @@ function Form(props) {
onSubmit(trimmedStringValues);
}, [props.formState.isLoading, props.network.isOffline, props.enabledWhenOffline, inputValues, onValidate, onSubmit]);
+ /**
+ * Resets the form
+ */
+ const resetForm = useCallback(
+ (optionalValue) => {
+ _.each(inputValues, (inputRef, inputID) => {
+ setInputValues((prevState) => {
+ const copyPrevState = _.clone(prevState);
+
+ touchedInputs.current[inputID] = false;
+ copyPrevState[inputID] = optionalValue[inputID] || '';
+
+ return copyPrevState;
+ });
+ });
+ setErrors({});
+ },
+ [inputValues],
+ );
+
+ useImperativeHandle(forwardedRef, () => ({
+ resetForm,
+ }));
+
/**
* Loops over Form's children and automatically supplies Form props to them
*
@@ -464,7 +493,9 @@ function Form(props) {
containerStyles={[styles.mh0, styles.mt5, styles.flex1, ...props.submitButtonStyles]}
enabledWhenOffline={props.enabledWhenOffline}
isSubmitActionDangerous={props.isSubmitActionDangerous}
+ useSmallerSubmitButtonSize={props.useSmallerSubmitButtonSize}
disablePressOnEnter
+ errorMessageStyle={props.errorMessageStyle}
/>
)}
@@ -474,6 +505,8 @@ function Form(props) {
props.style,
props.isSubmitButtonVisible,
props.submitButtonText,
+ props.useSmallerSubmitButtonSize,
+ props.errorMessageStyle,
props.formState.errorFields,
props.formState.isLoading,
props.footerContent,
@@ -539,7 +572,7 @@ function Form(props) {
}
);
-}
+});
Form.displayName = 'Form';
Form.propTypes = propTypes;
diff --git a/src/components/Form/FormProvider.js b/src/components/Form/FormProvider.js
index 63953d8303db..50b24e368fc6 100644
--- a/src/components/Form/FormProvider.js
+++ b/src/components/Form/FormProvider.js
@@ -1,6 +1,6 @@
import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
-import React, {createRef, useCallback, useMemo, useRef, useState} from 'react';
+import React, {createRef, forwardRef, useCallback, useImperativeHandle, useMemo, useRef, useState} from 'react';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import networkPropTypes from '@components/networkPropTypes';
@@ -109,250 +109,283 @@ function getInitialValueByType(valueType) {
}
}
-function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnChange, children, formState, network, enabledWhenOffline, draftValues, onSubmit, ...rest}) {
- const inputRefs = useRef({});
- const touchedInputs = useRef({});
- const [inputValues, setInputValues] = useState(() => ({...draftValues}));
- const [errors, setErrors] = useState({});
- const hasServerError = useMemo(() => Boolean(formState) && !_.isEmpty(formState.errors), [formState]);
+const FormProvider = forwardRef(
+ ({validate, formID, shouldValidateOnBlur, shouldValidateOnChange, children, formState, network, enabledWhenOffline, draftValues, onSubmit, ...rest}, forwardedRef) => {
+ const inputRefs = useRef({});
+ const touchedInputs = useRef({});
+ const [inputValues, setInputValues] = useState(() => ({...draftValues}));
+ const [errors, setErrors] = useState({});
+ const hasServerError = useMemo(() => Boolean(formState) && !_.isEmpty(formState.errors), [formState]);
- const onValidate = useCallback(
- (values, shouldClearServerError = true) => {
- const trimmedStringValues = ValidationUtils.prepareValues(values);
+ const onValidate = useCallback(
+ (values, shouldClearServerError = true) => {
+ const trimmedStringValues = ValidationUtils.prepareValues(values);
- if (shouldClearServerError) {
- FormActions.setErrors(formID, null);
- }
- FormActions.setErrorFields(formID, null);
+ if (shouldClearServerError) {
+ FormActions.setErrors(formID, null);
+ }
+ FormActions.setErrorFields(formID, null);
- const validateErrors = validate(values) || {};
+ const validateErrors = validate(trimmedStringValues) || {};
- // Validate the input for html tags. It should supercede any other error
- _.each(trimmedStringValues, (inputValue, inputID) => {
- // If the input value is empty OR is non-string, we don't need to validate it for HTML tags
- if (!inputValue || !_.isString(inputValue)) {
- return;
- }
- const foundHtmlTagIndex = inputValue.search(CONST.VALIDATE_FOR_HTML_TAG_REGEX);
- const leadingSpaceIndex = inputValue.search(CONST.VALIDATE_FOR_LEADINGSPACES_HTML_TAG_REGEX);
+ // Validate the input for html tags. It should supercede any other error
+ _.each(trimmedStringValues, (inputValue, inputID) => {
+ // If the input value is empty OR is non-string, we don't need to validate it for HTML tags
+ if (!inputValue || !_.isString(inputValue)) {
+ return;
+ }
+ const foundHtmlTagIndex = inputValue.search(CONST.VALIDATE_FOR_HTML_TAG_REGEX);
+ const leadingSpaceIndex = inputValue.search(CONST.VALIDATE_FOR_LEADINGSPACES_HTML_TAG_REGEX);
- // Return early if there are no HTML characters
- if (leadingSpaceIndex === -1 && foundHtmlTagIndex === -1) {
- return;
- }
+ // Return early if there are no HTML characters
+ if (leadingSpaceIndex === -1 && foundHtmlTagIndex === -1) {
+ return;
+ }
- const matchedHtmlTags = inputValue.match(CONST.VALIDATE_FOR_HTML_TAG_REGEX);
- let isMatch = _.some(CONST.WHITELISTED_TAGS, (r) => r.test(inputValue));
- // Check for any matches that the original regex (foundHtmlTagIndex) matched
- if (matchedHtmlTags) {
- // Check if any matched inputs does not match in WHITELISTED_TAGS list and return early if needed.
- for (let i = 0; i < matchedHtmlTags.length; i++) {
- const htmlTag = matchedHtmlTags[i];
- isMatch = _.some(CONST.WHITELISTED_TAGS, (r) => r.test(htmlTag));
- if (!isMatch) {
- break;
+ const matchedHtmlTags = inputValue.match(CONST.VALIDATE_FOR_HTML_TAG_REGEX);
+ let isMatch = _.some(CONST.WHITELISTED_TAGS, (r) => r.test(inputValue));
+ // Check for any matches that the original regex (foundHtmlTagIndex) matched
+ if (matchedHtmlTags) {
+ // Check if any matched inputs does not match in WHITELISTED_TAGS list and return early if needed.
+ for (let i = 0; i < matchedHtmlTags.length; i++) {
+ const htmlTag = matchedHtmlTags[i];
+ isMatch = _.some(CONST.WHITELISTED_TAGS, (r) => r.test(htmlTag));
+ if (!isMatch) {
+ break;
+ }
}
}
+
+ if (isMatch && leadingSpaceIndex === -1) {
+ return;
+ }
+
+ // Add a validation error here because it is a string value that contains HTML characters
+ validateErrors[inputID] = 'common.error.invalidCharacter';
+ });
+
+ if (!_.isObject(validateErrors)) {
+ throw new Error('Validate callback must return an empty object or an object with shape {inputID: error}');
}
- // Add a validation error here because it is a string value that contains HTML characters
- validateErrors[inputID] = 'common.error.invalidCharacter';
- });
- if (!_.isObject(validateErrors)) {
- throw new Error('Validate callback must return an empty object or an object with shape {inputID: error}');
- }
+ const touchedInputErrors = _.pick(validateErrors, (inputValue, inputID) => Boolean(touchedInputs.current[inputID]));
- const touchedInputErrors = _.pick(validateErrors, (inputValue, inputID) => Boolean(touchedInputs.current[inputID]));
+ if (!_.isEqual(errors, touchedInputErrors)) {
+ setErrors(touchedInputErrors);
+ }
- if (!_.isEqual(errors, touchedInputErrors)) {
- setErrors(touchedInputErrors);
+ return touchedInputErrors;
+ },
+ [errors, formID, validate],
+ );
+
+ /**
+ * @param {String} inputID - The inputID of the input being touched
+ */
+ const setTouchedInput = useCallback(
+ (inputID) => {
+ touchedInputs.current[inputID] = true;
+ },
+ [touchedInputs],
+ );
+
+ const submit = useCallback(() => {
+ // Return early if the form is already submitting to avoid duplicate submission
+ if (formState.isLoading) {
+ return;
}
- return touchedInputErrors;
- },
- [errors, formID, validate],
- );
-
- /**
- * @param {String} inputID - The inputID of the input being touched
- */
- const setTouchedInput = useCallback(
- (inputID) => {
- touchedInputs.current[inputID] = true;
- },
- [touchedInputs],
- );
-
- const submit = useCallback(() => {
- // Return early if the form is already submitting to avoid duplicate submission
- if (formState.isLoading) {
- return;
- }
-
- // Prepare values before submitting
- const trimmedStringValues = ValidationUtils.prepareValues(inputValues);
-
- // Touches all form inputs so we can validate the entire form
- _.each(inputRefs.current, (inputRef, inputID) => (touchedInputs.current[inputID] = true));
-
- // Validate form and return early if any errors are found
- if (!_.isEmpty(onValidate(trimmedStringValues))) {
- return;
- }
-
- // Do not submit form if network is offline and the form is not enabled when offline
- if (network.isOffline && !enabledWhenOffline) {
- return;
- }
-
- onSubmit(trimmedStringValues);
- }, [enabledWhenOffline, formState.isLoading, inputValues, network.isOffline, onSubmit, onValidate]);
-
- const registerInput = useCallback(
- (inputID, propsToParse = {}) => {
- const newRef = inputRefs.current[inputID] || propsToParse.ref || createRef();
- if (inputRefs.current[inputID] !== newRef) {
- inputRefs.current[inputID] = newRef;
+ // Prepare values before submitting
+ const trimmedStringValues = ValidationUtils.prepareValues(inputValues);
+
+ // Touches all form inputs so we can validate the entire form
+ _.each(inputRefs.current, (inputRef, inputID) => (touchedInputs.current[inputID] = true));
+
+ // Validate form and return early if any errors are found
+ if (!_.isEmpty(onValidate(trimmedStringValues))) {
+ return;
}
- if (!_.isUndefined(propsToParse.value)) {
- inputValues[inputID] = propsToParse.value;
- } else if (propsToParse.shouldSaveDraft && !_.isUndefined(draftValues[inputID]) && _.isUndefined(inputValues[inputID])) {
- inputValues[inputID] = draftValues[inputID];
- } else if (propsToParse.shouldUseDefaultValue && _.isUndefined(inputValues[inputID])) {
- // We force the form to set the input value from the defaultValue props if there is a saved valid value
- inputValues[inputID] = propsToParse.defaultValue;
- } else if (_.isUndefined(inputValues[inputID])) {
- // We want to initialize the input value if it's undefined
- inputValues[inputID] = _.isUndefined(propsToParse.defaultValue) ? getInitialValueByType(propsToParse.valueType) : propsToParse.defaultValue;
+ // Do not submit form if network is offline and the form is not enabled when offline
+ if (network.isOffline && !enabledWhenOffline) {
+ return;
}
- const errorFields = lodashGet(formState, 'errorFields', {});
- const fieldErrorMessage =
- _.chain(errorFields[inputID])
- .keys()
- .sortBy()
- .reverse()
- .map((key) => errorFields[inputID][key])
- .first()
- .value() || '';
-
- return {
- ...propsToParse,
- ref:
- typeof propsToParse.ref === 'function'
- ? (node) => {
- propsToParse.ref(node);
- newRef.current = node;
- }
- : newRef,
- inputID,
- key: propsToParse.key || inputID,
- errorText: errors[inputID] || fieldErrorMessage,
- value: inputValues[inputID],
- // As the text input is controlled, we never set the defaultValue prop
- // as this is already happening by the value prop.
- defaultValue: undefined,
- onTouched: (event) => {
- if (!propsToParse.shouldSetTouchedOnBlurOnly) {
- setTimeout(() => {
- setTouchedInput(inputID);
- }, VALIDATE_DELAY);
- }
- if (_.isFunction(propsToParse.onTouched)) {
- propsToParse.onTouched(event);
- }
- },
- onPress: (event) => {
- if (!propsToParse.shouldSetTouchedOnBlurOnly) {
- setTimeout(() => {
- setTouchedInput(inputID);
- }, VALIDATE_DELAY);
- }
- if (_.isFunction(propsToParse.onPress)) {
- propsToParse.onPress(event);
- }
- },
- onPressOut: (event) => {
- // To prevent validating just pressed inputs, we need to set the touched input right after
- // onValidate and to do so, we need to delays setTouchedInput of the same amount of time
- // as the onValidate is delayed
- if (!propsToParse.shouldSetTouchedOnBlurOnly) {
- setTimeout(() => {
- setTouchedInput(inputID);
- }, VALIDATE_DELAY);
- }
- if (_.isFunction(propsToParse.onPressIn)) {
- propsToParse.onPressIn(event);
- }
- },
- onBlur: (event) => {
- // Only run validation when user proactively blurs the input.
- if (Visibility.isVisible() && Visibility.hasFocus()) {
- const relatedTargetId = lodashGet(event, 'nativeEvent.relatedTarget.id');
- // We delay the validation in order to prevent Checkbox loss of focus when
- // the user is focusing a TextInput and proceeds to toggle a CheckBox in
- // web and mobile web platforms.
-
- setTimeout(() => {
- if (relatedTargetId && _.includes([CONST.OVERLAY.BOTTOM_BUTTON_NATIVE_ID, CONST.OVERLAY.TOP_BUTTON_NATIVE_ID, CONST.BACK_BUTTON_NATIVE_ID], relatedTargetId)) {
- return;
- }
- setTouchedInput(inputID);
- if (shouldValidateOnBlur) {
- onValidate(inputValues, !hasServerError);
- }
- }, VALIDATE_DELAY);
- }
+ onSubmit(trimmedStringValues);
+ }, [enabledWhenOffline, formState.isLoading, inputValues, network.isOffline, onSubmit, onValidate]);
- if (_.isFunction(propsToParse.onBlur)) {
- propsToParse.onBlur(event);
- }
- },
- onInputChange: (value, key) => {
- const inputKey = key || inputID;
+ /**
+ * Resets the form
+ */
+ const resetForm = useCallback(
+ (optionalValue) => {
+ _.each(inputValues, (inputRef, inputID) => {
setInputValues((prevState) => {
- const newState = {
- ...prevState,
- [inputKey]: value,
- };
+ const copyPrevState = _.clone(prevState);
- if (shouldValidateOnChange) {
- onValidate(newState);
- }
- return newState;
+ touchedInputs.current[inputID] = false;
+ copyPrevState[inputID] = optionalValue[inputID] || '';
+
+ return copyPrevState;
});
+ });
+ setErrors({});
+ },
+ [inputValues],
+ );
+ useImperativeHandle(forwardedRef, () => ({
+ resetForm,
+ }));
+
+ const registerInput = useCallback(
+ (inputID, propsToParse = {}) => {
+ const newRef = inputRefs.current[inputID] || propsToParse.ref || createRef();
+ if (inputRefs.current[inputID] !== newRef) {
+ inputRefs.current[inputID] = newRef;
+ }
- if (propsToParse.shouldSaveDraft) {
- FormActions.setDraftValues(formID, {[inputKey]: value});
- }
+ if (!_.isUndefined(propsToParse.value)) {
+ inputValues[inputID] = propsToParse.value;
+ } else if (propsToParse.shouldSaveDraft && !_.isUndefined(draftValues[inputID]) && _.isUndefined(inputValues[inputID])) {
+ inputValues[inputID] = draftValues[inputID];
+ } else if (propsToParse.shouldUseDefaultValue && _.isUndefined(inputValues[inputID])) {
+ // We force the form to set the input value from the defaultValue props if there is a saved valid value
+ inputValues[inputID] = propsToParse.defaultValue;
+ } else if (_.isUndefined(inputValues[inputID])) {
+ // We want to initialize the input value if it's undefined
+ inputValues[inputID] = _.isUndefined(propsToParse.defaultValue) ? getInitialValueByType(propsToParse.valueType) : propsToParse.defaultValue;
+ }
- if (_.isFunction(propsToParse.onValueChange)) {
- propsToParse.onValueChange(value, inputKey);
- }
- },
- };
- },
- [draftValues, formID, errors, formState, hasServerError, inputValues, onValidate, setTouchedInput, shouldValidateOnBlur, shouldValidateOnChange],
- );
- const value = useMemo(() => ({registerInput}), [registerInput]);
-
- return (
-
- {/* eslint-disable react/jsx-props-no-spreading */}
-
- {_.isFunction(children) ? children({inputValues}) : children}
-
-
- );
-}
+ const errorFields = lodashGet(formState, 'errorFields', {});
+ const fieldErrorMessage =
+ _.chain(errorFields[inputID])
+ .keys()
+ .sortBy()
+ .reverse()
+ .map((key) => errorFields[inputID][key])
+ .first()
+ .value() || '';
+
+ return {
+ ...propsToParse,
+ ref:
+ typeof propsToParse.ref === 'function'
+ ? (node) => {
+ propsToParse.ref(node);
+ newRef.current = node;
+ }
+ : newRef,
+ inputID,
+ key: propsToParse.key || inputID,
+ errorText: errors[inputID] || fieldErrorMessage,
+ value: inputValues[inputID],
+ // As the text input is controlled, we never set the defaultValue prop
+ // as this is already happening by the value prop.
+ defaultValue: undefined,
+ onTouched: (event) => {
+ if (!propsToParse.shouldSetTouchedOnBlurOnly) {
+ setTimeout(() => {
+ setTouchedInput(inputID);
+ }, VALIDATE_DELAY);
+ }
+ if (_.isFunction(propsToParse.onTouched)) {
+ propsToParse.onTouched(event);
+ }
+ },
+ onPress: (event) => {
+ if (!propsToParse.shouldSetTouchedOnBlurOnly) {
+ setTimeout(() => {
+ setTouchedInput(inputID);
+ }, VALIDATE_DELAY);
+ }
+ if (_.isFunction(propsToParse.onPress)) {
+ propsToParse.onPress(event);
+ }
+ },
+ onPressOut: (event) => {
+ // To prevent validating just pressed inputs, we need to set the touched input right after
+ // onValidate and to do so, we need to delays setTouchedInput of the same amount of time
+ // as the onValidate is delayed
+ if (!propsToParse.shouldSetTouchedOnBlurOnly) {
+ setTimeout(() => {
+ setTouchedInput(inputID);
+ }, VALIDATE_DELAY);
+ }
+ if (_.isFunction(propsToParse.onPressIn)) {
+ propsToParse.onPressIn(event);
+ }
+ },
+ onBlur: (event) => {
+ // Only run validation when user proactively blurs the input.
+ if (Visibility.isVisible() && Visibility.hasFocus()) {
+ const relatedTargetId = lodashGet(event, 'nativeEvent.relatedTarget.id');
+ // We delay the validation in order to prevent Checkbox loss of focus when
+ // the user is focusing a TextInput and proceeds to toggle a CheckBox in
+ // web and mobile web platforms.
+
+ setTimeout(() => {
+ if (
+ relatedTargetId &&
+ _.includes([CONST.OVERLAY.BOTTOM_BUTTON_NATIVE_ID, CONST.OVERLAY.TOP_BUTTON_NATIVE_ID, CONST.BACK_BUTTON_NATIVE_ID], relatedTargetId)
+ ) {
+ return;
+ }
+ setTouchedInput(inputID);
+ if (shouldValidateOnBlur) {
+ onValidate(inputValues, !hasServerError);
+ }
+ }, VALIDATE_DELAY);
+ }
+
+ if (_.isFunction(propsToParse.onBlur)) {
+ propsToParse.onBlur(event);
+ }
+ },
+ onInputChange: (value, key) => {
+ const inputKey = key || inputID;
+ setInputValues((prevState) => {
+ const newState = {
+ ...prevState,
+ [inputKey]: value,
+ };
+
+ if (shouldValidateOnChange) {
+ onValidate(newState);
+ }
+ return newState;
+ });
+
+ if (propsToParse.shouldSaveDraft) {
+ FormActions.setDraftValues(formID, {[inputKey]: value});
+ }
+
+ if (_.isFunction(propsToParse.onValueChange)) {
+ propsToParse.onValueChange(value, inputKey);
+ }
+ },
+ };
+ },
+ [draftValues, formID, errors, formState, hasServerError, inputValues, onValidate, setTouchedInput, shouldValidateOnBlur, shouldValidateOnChange],
+ );
+ const value = useMemo(() => ({registerInput}), [registerInput]);
+
+ return (
+
+ {/* eslint-disable react/jsx-props-no-spreading */}
+
+ {_.isFunction(children) ? children({inputValues}) : children}
+
+
+ );
+ },
+);
FormProvider.displayName = 'Form';
FormProvider.propTypes = propTypes;
diff --git a/src/components/Form/FormWrapper.js b/src/components/Form/FormWrapper.js
index 638b6e5f8d19..da34262a8af8 100644
--- a/src/components/Form/FormWrapper.js
+++ b/src/components/Form/FormWrapper.js
@@ -8,9 +8,9 @@ import FormSubmit from '@components/FormSubmit';
import refPropTypes from '@components/refPropTypes';
import SafeAreaConsumer from '@components/SafeAreaConsumer';
import ScrollViewWithContext from '@components/ScrollViewWithContext';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as ErrorUtils from '@libs/ErrorUtils';
import stylePropTypes from '@styles/stylePropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import errorsPropType from './errorsPropType';
const propTypes = {
diff --git a/src/components/FormAlertWithSubmitButton.js b/src/components/FormAlertWithSubmitButton.js
index b16a4d2a08ee..86e88c27b388 100644
--- a/src/components/FormAlertWithSubmitButton.js
+++ b/src/components/FormAlertWithSubmitButton.js
@@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
import React from 'react';
import {View} from 'react-native';
import _ from 'underscore';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import Button from './Button';
import FormAlertWrapper from './FormAlertWrapper';
@@ -50,6 +50,12 @@ const propTypes = {
/** Styles for the button */
// eslint-disable-next-line react/forbid-prop-types
buttonStyles: PropTypes.arrayOf(PropTypes.object),
+
+ /** Whether to use a smaller submit button size */
+ useSmallerSubmitButtonSize: PropTypes.bool,
+
+ /** Style for the error message for submit button */
+ errorMessageStyle: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]),
};
const defaultProps = {
@@ -62,8 +68,10 @@ const defaultProps = {
enabledWhenOffline: false,
disablePressOnEnter: false,
isSubmitActionDangerous: false,
+ useSmallerSubmitButtonSize: false,
footerContent: null,
buttonStyles: [],
+ errorMessageStyle: [],
};
function FormAlertWithSubmitButton(props) {
@@ -77,6 +85,7 @@ function FormAlertWithSubmitButton(props) {
isMessageHtml={props.isMessageHtml}
message={props.message}
onFixTheErrorsLinkPressed={props.onFixTheErrorsLinkPressed}
+ errorMessageStyle={props.errorMessageStyle}
>
{(isOffline) => (
@@ -87,6 +96,7 @@ function FormAlertWithSubmitButton(props) {
text={props.buttonText}
style={buttonStyles}
danger={props.isSubmitActionDangerous}
+ medium={props.useSmallerSubmitButtonSize}
/>
) : (
)}
{props.footerContent}
diff --git a/src/components/FormAlertWrapper.js b/src/components/FormAlertWrapper.js
deleted file mode 100644
index c577048c0a1b..000000000000
--- a/src/components/FormAlertWrapper.js
+++ /dev/null
@@ -1,91 +0,0 @@
-import PropTypes from 'prop-types';
-import React from 'react';
-import {View} from 'react-native';
-import _ from 'underscore';
-import compose from '@libs/compose';
-import useThemeStyles from '@styles/useThemeStyles';
-import FormHelpMessage from './FormHelpMessage';
-import networkPropTypes from './networkPropTypes';
-import {withNetwork} from './OnyxProvider';
-import RenderHTML from './RenderHTML';
-import Text from './Text';
-import TextLink from './TextLink';
-import withLocalize, {withLocalizePropTypes} from './withLocalize';
-
-const propTypes = {
- /** Wrapped child components */
- children: PropTypes.func.isRequired,
-
- /** Styles for container element */
- // eslint-disable-next-line react/forbid-prop-types
- containerStyles: PropTypes.arrayOf(PropTypes.object),
-
- /** Whether to show the alert text */
- isAlertVisible: PropTypes.bool,
-
- /** Whether message is in html format */
- isMessageHtml: PropTypes.bool,
-
- /** Error message to display above button */
- message: PropTypes.string,
-
- /** Props to detect online status */
- network: networkPropTypes.isRequired,
-
- /** Callback fired when the "fix the errors" link is pressed */
- onFixTheErrorsLinkPressed: PropTypes.func,
-
- ...withLocalizePropTypes,
-};
-
-const defaultProps = {
- containerStyles: [],
- isAlertVisible: false,
- isMessageHtml: false,
- message: '',
- onFixTheErrorsLinkPressed: () => {},
-};
-
-// The FormAlertWrapper offers a standardized way of showing error messages and offline functionality.
-//
-// This component takes other components as a child prop. It will then render any wrapped components as a function using "render props",
-// and passes it a (bool) isOffline parameter. Child components can then use the isOffline variable to determine offline behavior.
-function FormAlertWrapper(props) {
- const styles = useThemeStyles();
- let children;
- if (_.isEmpty(props.message)) {
- children = (
-
- {`${props.translate('common.please')} `}
-
- {props.translate('common.fixTheErrors')}
-
- {` ${props.translate('common.inTheFormBeforeContinuing')}.`}
-
- );
- } else if (props.isMessageHtml) {
- children = ${props.message}`} />;
- }
- return (
-
- {props.isAlertVisible && (
-
- {children}
-
- )}
- {props.children(props.network.isOffline)}
-
- );
-}
-
-FormAlertWrapper.propTypes = propTypes;
-FormAlertWrapper.defaultProps = defaultProps;
-FormAlertWrapper.displayName = 'FormAlertWrapper';
-
-export default compose(withLocalize, withNetwork())(FormAlertWrapper);
diff --git a/src/components/FormAlertWrapper.tsx b/src/components/FormAlertWrapper.tsx
new file mode 100644
index 000000000000..a144bf069502
--- /dev/null
+++ b/src/components/FormAlertWrapper.tsx
@@ -0,0 +1,90 @@
+import React, {ReactNode} from 'react';
+import {StyleProp, View, ViewStyle} from 'react-native';
+import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
+import Network from '@src/types/onyx/Network';
+import FormHelpMessage from './FormHelpMessage';
+import {withNetwork} from './OnyxProvider';
+import RenderHTML from './RenderHTML';
+import Text from './Text';
+import TextLink from './TextLink';
+
+type FormAlertWrapperProps = {
+ /** Wrapped child components */
+ children: (isOffline?: boolean) => ReactNode;
+
+ /** Styles for container element */
+ containerStyles?: StyleProp;
+
+ /** Style for the error message for submit button */
+ errorMessageStyle?: StyleProp;
+
+ /** Whether to show the alert text */
+ isAlertVisible?: boolean;
+
+ /** Whether message is in html format */
+ isMessageHtml?: boolean;
+
+ /** Error message to display above button */
+ message?: string;
+
+ /** Props to detect online status */
+ network: Network;
+
+ /** Callback fired when the "fix the errors" link is pressed */
+ onFixTheErrorsLinkPressed?: () => void;
+};
+
+// The FormAlertWrapper offers a standardized way of showing error messages and offline functionality.
+//
+// This component takes other components as a child prop. It will then render any wrapped components as a function using "render props",
+// and passes it a (bool) isOffline parameter. Child components can then use the isOffline variable to determine offline behavior.
+function FormAlertWrapper({
+ children,
+ containerStyles,
+ errorMessageStyle,
+ isAlertVisible = false,
+ isMessageHtml = false,
+ message = '',
+ network,
+ onFixTheErrorsLinkPressed = () => {},
+}: FormAlertWrapperProps) {
+ const styles = useThemeStyles();
+ const {translate} = useLocalize();
+
+ let content;
+ if (!message?.length) {
+ content = (
+
+ {`${translate('common.please')} `}
+
+ {translate('common.fixTheErrors')}
+
+ {` ${translate('common.inTheFormBeforeContinuing')}.`}
+
+ );
+ } else if (isMessageHtml) {
+ content = ${message}`} />;
+ }
+
+ return (
+
+ {isAlertVisible && (
+
+ {content}
+
+ )}
+ {children(!!network.isOffline)}
+
+ );
+}
+
+FormAlertWrapper.displayName = 'FormAlertWrapper';
+
+export default withNetwork()(FormAlertWrapper);
diff --git a/src/components/FormHelpMessage.tsx b/src/components/FormHelpMessage.tsx
index 27a1f5827d75..43709b51db44 100644
--- a/src/components/FormHelpMessage.tsx
+++ b/src/components/FormHelpMessage.tsx
@@ -1,9 +1,9 @@
import isEmpty from 'lodash/isEmpty';
import React from 'react';
import {StyleProp, View, ViewStyle} from 'react-native';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as Localize from '@libs/Localize';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import Icon from './Icon';
import * as Expensicons from './Icon/Expensicons';
import Text from './Text';
@@ -20,9 +20,12 @@ type FormHelpMessageProps = {
/** Container style props */
style?: StyleProp;
+
+ /** Whether to show dot indicator */
+ shouldShowRedDotIndicator?: boolean;
};
-function FormHelpMessage({message = '', children, isError = true, style}: FormHelpMessageProps) {
+function FormHelpMessage({message = '', children, isError = true, style, shouldShowRedDotIndicator = true}: FormHelpMessageProps) {
const theme = useTheme();
const styles = useThemeStyles();
if (isEmpty(message) && isEmpty(children)) {
@@ -33,13 +36,15 @@ function FormHelpMessage({message = '', children, isError = true, style}: FormHe
return (
- {isError && (
+ {isError && shouldShowRedDotIndicator && (
)}
- {children ?? {translatedMessage}}
+
+ {children ?? {translatedMessage}}
+
);
}
diff --git a/src/components/FormScrollView.tsx b/src/components/FormScrollView.tsx
index c2f01e1d0511..4646a534e750 100644
--- a/src/components/FormScrollView.tsx
+++ b/src/components/FormScrollView.tsx
@@ -1,6 +1,6 @@
import React, {ForwardedRef} from 'react';
import {ScrollView, ScrollViewProps} from 'react-native';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
type FormScrollViewProps = ScrollViewProps & {
/** Form elements */
diff --git a/src/components/FullscreenLoadingIndicator.tsx b/src/components/FullscreenLoadingIndicator.tsx
index fc0687f6da18..2b5f7b3ada72 100644
--- a/src/components/FullscreenLoadingIndicator.tsx
+++ b/src/components/FullscreenLoadingIndicator.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import {ActivityIndicator, StyleProp, StyleSheet, View, ViewStyle} from 'react-native';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
type FullScreenLoadingIndicatorProps = {
style?: StyleProp;
diff --git a/src/components/GrowlNotification/GrowlNotificationContainer/index.js b/src/components/GrowlNotification/GrowlNotificationContainer/index.js
index 82672edb14c2..ccc404d415d7 100644
--- a/src/components/GrowlNotification/GrowlNotificationContainer/index.js
+++ b/src/components/GrowlNotification/GrowlNotificationContainer/index.js
@@ -1,7 +1,7 @@
import React from 'react';
import {Animated} from 'react-native';
import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import growlNotificationContainerPropTypes from './growlNotificationContainerPropTypes';
const propTypes = {
diff --git a/src/components/GrowlNotification/GrowlNotificationContainer/index.native.js b/src/components/GrowlNotification/GrowlNotificationContainer/index.native.js
index 457a9dce66d9..207033f8fac2 100644
--- a/src/components/GrowlNotification/GrowlNotificationContainer/index.native.js
+++ b/src/components/GrowlNotification/GrowlNotificationContainer/index.native.js
@@ -1,8 +1,8 @@
import React from 'react';
import {Animated} from 'react-native';
import useSafeAreaInsets from '@hooks/useSafeAreaInsets';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import growlNotificationContainerPropTypes from './growlNotificationContainerPropTypes';
const propTypes = {
diff --git a/src/components/GrowlNotification/index.js b/src/components/GrowlNotification/index.js
index faf1ec9cfa16..bcb4a8703b94 100644
--- a/src/components/GrowlNotification/index.js
+++ b/src/components/GrowlNotification/index.js
@@ -5,10 +5,10 @@ import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import * as Pressables from '@components/Pressable';
import Text from '@components/Text';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as Growl from '@libs/Growl';
import useNativeDriver from '@libs/useNativeDriver';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import GrowlNotificationContainer from './GrowlNotificationContainer';
diff --git a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js
index 9a2d7c673c5b..86ddf0a52bb3 100755
--- a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js
+++ b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js
@@ -2,9 +2,9 @@ import PropTypes from 'prop-types';
import React, {useMemo} from 'react';
import {defaultHTMLElementModels, RenderHTMLConfigProvider, TRenderEngineProvider} from 'react-native-render-html';
import _ from 'underscore';
+import useThemeStyles from '@hooks/useThemeStyles';
import convertToLTR from '@libs/convertToLTR';
-import singleFontFamily from '@styles/fontFamily/singleFontFamily';
-import useThemeStyles from '@styles/useThemeStyles';
+import singleFontFamily from '@styles/utils/fontFamily/singleFontFamily';
import * as HTMLEngineUtils from './htmlEngineUtils';
import htmlRenderers from './HTMLRenderers';
diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js
index 49642308a357..8fd907470c19 100644
--- a/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js
+++ b/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js
@@ -6,8 +6,8 @@ import AnchorForCommentsOnly from '@components/AnchorForCommentsOnly';
import * as HTMLEngineUtils from '@components/HTMLEngineProvider/htmlEngineUtils';
import Text from '@components/Text';
import useEnvironment from '@hooks/useEnvironment';
+import useThemeStyles from '@hooks/useThemeStyles';
import tryResolveUrlFromApiRoot from '@libs/tryResolveUrlFromApiRoot';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Link from '@userActions/Link';
import CONST from '@src/CONST';
import htmlRendererPropTypes from './htmlRendererPropTypes';
diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/CodeRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/CodeRenderer.js
index 9d101b8a5190..1932eaaf8a4f 100644
--- a/src/components/HTMLEngineProvider/HTMLRenderers/CodeRenderer.js
+++ b/src/components/HTMLEngineProvider/HTMLRenderers/CodeRenderer.js
@@ -3,7 +3,7 @@ import {splitBoxModelStyle} from 'react-native-render-html';
import _ from 'underscore';
import * as HTMLEngineUtils from '@components/HTMLEngineProvider/htmlEngineUtils';
import InlineCodeBlock from '@components/InlineCodeBlock';
-import useStyleUtils from '@styles/useStyleUtils';
+import useStyleUtils from '@hooks/useStyleUtils';
import htmlRendererPropTypes from './htmlRendererPropTypes';
function CodeRenderer(props) {
diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/EditedRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/EditedRenderer.js
index e97d01808a6e..9ff5fdecae13 100644
--- a/src/components/HTMLEngineProvider/HTMLRenderers/EditedRenderer.js
+++ b/src/components/HTMLEngineProvider/HTMLRenderers/EditedRenderer.js
@@ -2,9 +2,8 @@ import React from 'react';
import _ from 'underscore';
import Text from '@components/Text';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
-import editedLabelStyles from '@styles/editedLabelStyles';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import htmlRendererPropTypes from './htmlRendererPropTypes';
@@ -32,7 +31,7 @@ function EditedRenderer(props) {
{...defaultRendererProps}
fontSize={variables.fontSizeSmall}
color={theme.textSupporting}
- style={[editedLabelStyles, isPendingDelete && styles.offlineFeedback.deleted]}
+ style={[styles.editedLabelStyles, isPendingDelete && styles.offlineFeedback.deleted]}
>
{props.translate('reportActionCompose.edited')}
diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js
index f5ecc106d629..05016a25394a 100644
--- a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js
+++ b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js
@@ -5,10 +5,10 @@ import PressableWithoutFocus from '@components/Pressable/PressableWithoutFocus';
import {ShowContextMenuContext, showContextMenuForReport} from '@components/ShowContextMenuContext';
import ThumbnailImage from '@components/ThumbnailImage';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import * as ReportUtils from '@libs/ReportUtils';
import tryResolveUrlFromApiRoot from '@libs/tryResolveUrlFromApiRoot';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionHereRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/MentionHereRenderer.js
index 82769598d84a..93ede229876d 100644
--- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionHereRenderer.js
+++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionHereRenderer.js
@@ -2,7 +2,7 @@ import React from 'react';
import {TNodeChildrenRenderer} from 'react-native-render-html';
import _ from 'underscore';
import Text from '@components/Text';
-import useStyleUtils from '@styles/useStyleUtils';
+import useStyleUtils from '@hooks/useStyleUtils';
import htmlRendererPropTypes from './htmlRendererPropTypes';
function MentionHereRenderer(props) {
diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js
index 14580bf90bfb..11ffabe4fe6a 100644
--- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js
+++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js
@@ -8,13 +8,13 @@ import Text from '@components/Text';
import UserDetailsTooltip from '@components/UserDetailsTooltip';
import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails';
import useLocalize from '@hooks/useLocalize';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as LocalePhoneNumber from '@libs/LocalePhoneNumber';
import Navigation from '@libs/Navigation/Navigation';
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
import * as ReportUtils from '@libs/ReportUtils';
import personalDetailsPropType from '@pages/personalDetailsPropType';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import htmlRendererPropTypes from './htmlRendererPropTypes';
diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer/BasePreRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer/BasePreRenderer.js
index ea8d3858c49b..07954cc97a00 100644
--- a/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer/BasePreRenderer.js
+++ b/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer/BasePreRenderer.js
@@ -6,8 +6,8 @@ import htmlRendererPropTypes from '@components/HTMLEngineProvider/HTMLRenderers/
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
import {ShowContextMenuContext, showContextMenuForReport} from '@components/ShowContextMenuContext';
import withLocalize from '@components/withLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as ReportUtils from '@libs/ReportUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
const propTypes = {
diff --git a/src/components/Header.tsx b/src/components/Header.tsx
index 46fe1a25c920..c02f21d7c6f2 100644
--- a/src/components/Header.tsx
+++ b/src/components/Header.tsx
@@ -1,6 +1,6 @@
import React, {ReactElement} from 'react';
import {StyleProp, TextStyle, View} from 'react-native';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import EnvironmentBadge from './EnvironmentBadge';
import Text from './Text';
diff --git a/src/components/HeaderGap/index.desktop.js b/src/components/HeaderGap/index.desktop.js
deleted file mode 100644
index 07ea1ea6f48d..000000000000
--- a/src/components/HeaderGap/index.desktop.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import PropTypes from 'prop-types';
-import React, {memo} from 'react';
-import {View} from 'react-native';
-import withThemeStyles, {withThemeStylesPropTypes} from '@components/withThemeStyles';
-import compose from '@libs/compose';
-
-const propTypes = {
- /** Styles to apply to the HeaderGap */
- // eslint-disable-next-line react/forbid-prop-types
- styles: PropTypes.arrayOf(PropTypes.object),
- ...withThemeStylesPropTypes,
-};
-
-const defaultProps = {
- styles: [],
-};
-
-function HeaderGap(props) {
- return ;
-}
-
-HeaderGap.displayName = 'HeaderGap';
-HeaderGap.propTypes = propTypes;
-HeaderGap.defaultProps = defaultProps;
-export default compose(memo, withThemeStyles)(HeaderGap);
diff --git a/src/components/HeaderGap/index.desktop.tsx b/src/components/HeaderGap/index.desktop.tsx
new file mode 100644
index 000000000000..3687bb2e6b02
--- /dev/null
+++ b/src/components/HeaderGap/index.desktop.tsx
@@ -0,0 +1,13 @@
+import React, {memo} from 'react';
+import {View} from 'react-native';
+import useThemeStyles from '@hooks/useThemeStyles';
+import type {HeaderGapProps, HeaderGapReturnType} from './types';
+
+function HeaderGap({styles}: HeaderGapProps): HeaderGapReturnType {
+ const themeStyles = useThemeStyles();
+ return ;
+}
+
+HeaderGap.displayName = 'HeaderGap';
+
+export default memo(HeaderGap);
diff --git a/src/components/HeaderGap/index.js b/src/components/HeaderGap/index.js
deleted file mode 100644
index 35e6bf92fb5d..000000000000
--- a/src/components/HeaderGap/index.js
+++ /dev/null
@@ -1,6 +0,0 @@
-function HeaderGap() {
- return null;
-}
-
-HeaderGap.displayName = 'HeaderGap';
-export default HeaderGap;
diff --git a/src/components/HeaderGap/index.tsx b/src/components/HeaderGap/index.tsx
new file mode 100644
index 000000000000..191923efd7c8
--- /dev/null
+++ b/src/components/HeaderGap/index.tsx
@@ -0,0 +1,10 @@
+import type {HeaderGapProps, HeaderGapReturnType} from './types';
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+function HeaderGap({styles}: HeaderGapProps): HeaderGapReturnType {
+ return null;
+}
+
+HeaderGap.displayName = 'HeaderGap';
+
+export default HeaderGap;
diff --git a/src/components/HeaderGap/types.ts b/src/components/HeaderGap/types.ts
new file mode 100644
index 000000000000..55a202c2e48c
--- /dev/null
+++ b/src/components/HeaderGap/types.ts
@@ -0,0 +1,10 @@
+import {ReactNode} from 'react';
+import {StyleProp, ViewStyle} from 'react-native';
+
+type HeaderGapProps = {
+ styles?: StyleProp;
+};
+
+type HeaderGapReturnType = ReactNode;
+
+export type {HeaderGapProps, HeaderGapReturnType};
diff --git a/src/components/HeaderPageLayout.js b/src/components/HeaderPageLayout.js
index 27c9656abd1a..9ef5d4f83a06 100644
--- a/src/components/HeaderPageLayout.js
+++ b/src/components/HeaderPageLayout.js
@@ -3,11 +3,11 @@ import React, {useMemo} from 'react';
import {ScrollView, View} from 'react-native';
import _ from 'underscore';
import useNetwork from '@hooks/useNetwork';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as Browser from '@libs/Browser';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import FixedFooter from './FixedFooter';
import HeaderWithBackButton from './HeaderWithBackButton';
import headerWithBackButtonPropTypes from './HeaderWithBackButton/headerWithBackButtonPropTypes';
diff --git a/src/components/HeaderWithBackButton/index.js b/src/components/HeaderWithBackButton/index.js
index 9de8bd6a22c7..5c76d1665310 100755
--- a/src/components/HeaderWithBackButton/index.js
+++ b/src/components/HeaderWithBackButton/index.js
@@ -10,12 +10,12 @@ import ThreeDotsMenu from '@components/ThreeDotsMenu';
import Tooltip from '@components/Tooltip';
import useKeyboardState from '@hooks/useKeyboardState';
import useLocalize from '@hooks/useLocalize';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import useThrottledButtonState from '@hooks/useThrottledButtonState';
import useWaitForNavigation from '@hooks/useWaitForNavigation';
import getButtonState from '@libs/getButtonState';
import Navigation from '@libs/Navigation/Navigation';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import headerWithBackButtonPropTypes from './headerWithBackButtonPropTypes';
diff --git a/src/components/Icon/BankIcons.ts b/src/components/Icon/BankIcons.ts
index 5e4c0192ca86..5a843af6690b 100644
--- a/src/components/Icon/BankIcons.ts
+++ b/src/components/Icon/BankIcons.ts
@@ -1,7 +1,7 @@
import {SvgProps} from 'react-native-svg';
import GenericBank from '@assets/images/bankicons/generic-bank-account.svg';
import GenericBankCard from '@assets/images/cardicons/generic-bank-card.svg';
-import {type ThemeStyles} from '@styles/styles';
+import {type ThemeStyles} from '@styles/index';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import {BankIcon, BankName, BankNameKey} from '@src/types/onyx/Bank';
diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts
index b47daf0711b2..66a162bf2e5f 100644
--- a/src/components/Icon/Expensicons.ts
+++ b/src/components/Icon/Expensicons.ts
@@ -86,6 +86,7 @@ import Menu from '@assets/images/menu.svg';
import MoneyBag from '@assets/images/money-bag.svg';
import MoneyCircle from '@assets/images/money-circle.svg';
import Monitor from '@assets/images/monitor.svg';
+import NewExpensify from '@assets/images/new-expensify.svg';
import NewWindow from '@assets/images/new-window.svg';
import NewWorkspace from '@assets/images/new-workspace.svg';
import OfflineCloud from '@assets/images/offline-cloud.svg';
@@ -221,6 +222,7 @@ export {
MoneyBag,
MoneyCircle,
Monitor,
+ NewExpensify,
NewWindow,
NewWorkspace,
Offline,
diff --git a/src/components/Icon/index.tsx b/src/components/Icon/index.tsx
index 80abe1872c12..5f82421c0e8e 100644
--- a/src/components/Icon/index.tsx
+++ b/src/components/Icon/index.tsx
@@ -1,8 +1,8 @@
-import React, {PureComponent} from 'react';
+import React from 'react';
import {StyleProp, View, ViewStyle} from 'react-native';
-import withStyleUtils, {WithStyleUtilsProps} from '@components/withStyleUtils';
-import withTheme, {WithThemeProps} from '@components/withTheme';
-import withThemeStyles, {type WithThemeStylesProps} from '@components/withThemeStyles';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
import IconWrapperStyles from './IconWrapperStyles';
@@ -41,65 +41,63 @@ type IconProps = {
/** Additional styles to add to the Icon */
additionalStyles?: StyleProp;
-} & WithThemeStylesProps &
- WithThemeProps &
- WithStyleUtilsProps;
-
-// We must use a class component to create an animatable component with the Animated API
-// eslint-disable-next-line react/prefer-stateless-function
-class Icon extends PureComponent {
- // eslint-disable-next-line react/static-property-placement
- public static defaultProps = {
- width: variables.iconSizeNormal,
- height: variables.iconSizeNormal,
- fill: undefined,
- small: false,
- inline: false,
- additionalStyles: [],
- hovered: false,
- pressed: false,
- };
-
- render() {
- const width = this.props.small ? variables.iconSizeSmall : this.props.width;
- const height = this.props.small ? variables.iconSizeSmall : this.props.height;
- const iconStyles = [this.props.StyleUtils.getWidthAndHeightStyle(width ?? 0, height), IconWrapperStyles, this.props.themeStyles.pAbsolute, this.props.additionalStyles];
- const fill = this.props.fill ?? this.props.theme.icon;
+};
- if (this.props.inline) {
- return (
-
-
-
-
-
- );
- }
+function Icon({
+ src,
+ width = variables.iconSizeNormal,
+ height = variables.iconSizeNormal,
+ fill = undefined,
+ small = false,
+ inline = false,
+ hovered = false,
+ pressed = false,
+ additionalStyles = [],
+}: IconProps) {
+ const theme = useTheme();
+ const StyleUtils = useStyleUtils();
+ const styles = useThemeStyles();
+ const iconWidth = small ? variables.iconSizeSmall : width;
+ const iconHeight = small ? variables.iconSizeSmall : height;
+ const iconStyles = [StyleUtils.getWidthAndHeightStyle(width ?? 0, height), IconWrapperStyles, styles.pAbsolute, additionalStyles];
+ const iconFill = fill ?? theme.icon;
+ const IconComponent = src;
+ if (inline) {
return (
-
+
+
+
);
}
+
+ return (
+
+
+
+ );
}
-export default withTheme(withThemeStyles(withStyleUtils(Icon)));
+Icon.displayName = 'Icon';
+
+export default Icon;
diff --git a/src/components/Icon/svgs/LoungeAccessIcon.tsx b/src/components/Icon/svgs/LoungeAccessIcon.tsx
index 48b140da3bc8..e57573c439f1 100644
--- a/src/components/Icon/svgs/LoungeAccessIcon.tsx
+++ b/src/components/Icon/svgs/LoungeAccessIcon.tsx
@@ -1,6 +1,6 @@
import * as React from 'react';
import Svg, {G, Path, Polygon} from 'react-native-svg';
-import useTheme from '@styles/themes/useTheme';
+import useTheme from '@hooks/useTheme';
type LoungeAccessIconProps = {
/** The fill color for the icon. Can be hex, rgb, rgba, or valid react-native named color such as 'red' or 'blue'. */
diff --git a/src/components/IllustratedHeaderPageLayout.js b/src/components/IllustratedHeaderPageLayout.js
index 0557c7a6ca7b..9980d8a7879a 100644
--- a/src/components/IllustratedHeaderPageLayout.js
+++ b/src/components/IllustratedHeaderPageLayout.js
@@ -1,7 +1,7 @@
import PropTypes from 'prop-types';
import React from 'react';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import HeaderPageLayout from './HeaderPageLayout';
import headerWithBackButtonPropTypes from './HeaderWithBackButton/headerWithBackButtonPropTypes';
import Lottie from './Lottie';
diff --git a/src/components/ImageView/index.js b/src/components/ImageView/index.js
index 7ec5b0c33432..f16b37f328f5 100644
--- a/src/components/ImageView/index.js
+++ b/src/components/ImageView/index.js
@@ -1,35 +1,13 @@
-import PropTypes from 'prop-types';
import React, {useCallback, useEffect, useRef, useState} from 'react';
import {View} from 'react-native';
import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
import Image from '@components/Image';
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
-
-const propTypes = {
- /** Whether source url requires authentication */
- isAuthTokenRequired: PropTypes.bool,
-
- /** Handles scale changed event in image zoom component. Used on native only */
- // eslint-disable-next-line react/no-unused-prop-types
- onScaleChanged: PropTypes.func.isRequired,
-
- /** URL to full-sized image */
- url: PropTypes.string.isRequired,
-
- /** image file name */
- fileName: PropTypes.string.isRequired,
-
- onError: PropTypes.func,
-};
-
-const defaultProps = {
- isAuthTokenRequired: false,
- onError: () => {},
-};
+import {imageViewDefaultProps, imageViewPropTypes} from './propTypes';
function ImageView({isAuthTokenRequired, url, fileName, onError}) {
const styles = useThemeStyles();
@@ -283,8 +261,8 @@ function ImageView({isAuthTokenRequired, url, fileName, onError}) {
);
}
-ImageView.propTypes = propTypes;
-ImageView.defaultProps = defaultProps;
+ImageView.propTypes = imageViewPropTypes;
+ImageView.defaultProps = imageViewDefaultProps;
ImageView.displayName = 'ImageView';
export default ImageView;
diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js
index 5ebd03170d92..98349b213aa5 100644
--- a/src/components/ImageView/index.native.js
+++ b/src/components/ImageView/index.native.js
@@ -1,26 +1,15 @@
import PropTypes from 'prop-types';
-import React, {useEffect, useRef, useState} from 'react';
-import {PanResponder, View} from 'react-native';
-import ImageZoom from 'react-native-image-pan-zoom';
-import _ from 'underscore';
-import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
-import Image from '@components/Image';
-import useWindowDimensions from '@hooks/useWindowDimensions';
-import useThemeStyles from '@styles/useThemeStyles';
-import variables from '@styles/variables';
+import React from 'react';
+import Lightbox from '@components/Lightbox';
+import {zoomRangeDefaultProps, zoomRangePropTypes} from '@components/MultiGestureCanvas/propTypes';
+import {imageViewDefaultProps, imageViewPropTypes} from './propTypes';
/**
* On the native layer, we use a image library to handle zoom functionality
*/
const propTypes = {
- /** Whether source url requires authentication */
- isAuthTokenRequired: PropTypes.bool,
-
- /** URL to full-sized image */
- url: PropTypes.string.isRequired,
-
- /** Handles scale changed event in image zoom component. Used on native only */
- onScaleChanged: PropTypes.func.isRequired,
+ ...imageViewPropTypes,
+ ...zoomRangePropTypes,
/** Function for handle on press */
onPress: PropTypes.func,
@@ -30,214 +19,29 @@ const propTypes = {
};
const defaultProps = {
- isAuthTokenRequired: false,
+ ...imageViewDefaultProps,
+ ...zoomRangeDefaultProps,
+
onPress: () => {},
style: {},
};
-// Use the default double click interval from the ImageZoom library
-// https://github.com/ascoders/react-native-image-zoom/blob/master/src/image-zoom/image-zoom.type.ts#L79
-const DOUBLE_CLICK_INTERVAL = 175;
-
-function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style}) {
- const styles = useThemeStyles();
- const {windowWidth, windowHeight} = useWindowDimensions();
-
- const [isLoading, setIsLoading] = useState(true);
- const [imageDimensions, setImageDimensions] = useState({
- width: 0,
- height: 0,
- });
- const [containerHeight, setContainerHeight] = useState(null);
-
- const imageZoomScale = useRef(1);
- const lastClickTime = useRef(0);
- const numberOfTouches = useRef(0);
- const zoom = useRef(null);
-
- /**
- * Updates the amount of active touches on the PanResponder on our ImageZoom overlay View
- *
- * @param {Event} e
- * @param {GestureState} gestureState
- * @returns {Boolean}
- */
- const updatePanResponderTouches = (e, gestureState) => {
- if (_.isNumber(gestureState.numberActiveTouches)) {
- numberOfTouches.current = gestureState.numberActiveTouches;
- }
-
- // We don't need to set the panResponder since all we care about is checking the gestureState, so return false
- return false;
- };
-
- // PanResponder used to capture how many touches are active on the attachment image
- const panResponder = useRef(
- PanResponder.create({
- onStartShouldSetPanResponder: updatePanResponderTouches,
- }),
- ).current;
-
- /**
- * When the url changes and the image must load again,
- * this resets the zoom to ensure the next image loads with the correct dimensions.
- */
- const resetImageZoom = () => {
- if (imageZoomScale.current !== 1) {
- imageZoomScale.current = 1;
- }
-
- if (zoom.current) {
- zoom.current.centerOn({
- x: 0,
- y: 0,
- scale: 1,
- duration: 0,
- });
- }
- };
-
- const imageLoadingStart = () => {
- if (isLoading) {
- return;
- }
-
- resetImageZoom();
- setImageDimensions({
- width: 0,
- height: 0,
- });
- setIsLoading(true);
- };
-
- useEffect(() => {
- imageLoadingStart();
- // eslint-disable-next-line react-hooks/exhaustive-deps -- this effect only needs to run when the url changes
- }, [url]);
+function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style, zoomRange, onError, isUsedInCarousel, isSingleCarouselItem, carouselItemIndex, carouselActiveItemIndex}) {
+ const hasSiblingCarouselItems = isUsedInCarousel && !isSingleCarouselItem;
- /**
- * The `ImageZoom` component requires image dimensions which
- * are calculated here from the natural image dimensions produced by
- * the `onLoad` event
- *
- * @param {Object} nativeEvent
- */
- const configureImageZoom = ({nativeEvent}) => {
- let imageZoomWidth = nativeEvent.width;
- let imageZoomHeight = nativeEvent.height;
- const roundedContainerWidth = Math.round(windowWidth);
- const roundedContainerHeight = Math.round(containerHeight || windowHeight);
-
- const aspectRatio = Math.min(roundedContainerHeight / imageZoomHeight, roundedContainerWidth / imageZoomWidth);
-
- imageZoomHeight *= aspectRatio;
- imageZoomWidth *= aspectRatio;
-
- // Resize the image to max dimensions possible on the Native platforms to prevent crashes on Android. To keep the same behavior, apply to IOS as well.
- const maxDimensionsScale = 11;
- imageZoomWidth = Math.min(imageZoomWidth, roundedContainerWidth * maxDimensionsScale);
- imageZoomHeight = Math.min(imageZoomHeight, roundedContainerHeight * maxDimensionsScale);
-
- setImageDimensions({
- height: imageZoomHeight,
- width: imageZoomWidth,
- });
- setIsLoading(false);
- };
-
- const configurePanResponder = () => {
- const currentTimestamp = new Date().getTime();
- const isDoubleClick = currentTimestamp - lastClickTime.current <= DOUBLE_CLICK_INTERVAL;
- lastClickTime.current = currentTimestamp;
-
- // Let ImageZoom handle the event if the tap is more than one touchPoint or if we are zoomed in
- if (numberOfTouches.current === 2 || imageZoomScale.current !== 1) {
- return true;
- }
-
- // When we have a double click and the zoom scale is 1 then programmatically zoom the image
- // but let the tap fall through to the parent so we can register a swipe down to dismiss
- if (isDoubleClick) {
- zoom.current.centerOn({
- x: 0,
- y: 0,
- scale: 2,
- duration: 100,
- });
-
- // onMove will be called after the zoom animation.
- // So it's possible to zoom and swipe and stuck in between the images.
- // Sending scale just when we actually trigger the animation makes this nearly impossible.
- // you should be really fast to catch in between state updates.
- // And this lucky case will be fixed by migration to UI thread only code
- // with gesture handler and reanimated.
- onScaleChanged(2);
- }
-
- // We must be either swiping down or double tapping since we are at zoom scale 1
- return false;
- };
-
- // Default windowHeight accounts for the modal header height
- const calculatedWindowHeight = windowHeight - variables.contentHeaderHeight;
- const hasImageDimensions = imageDimensions.width !== 0 && imageDimensions.height !== 0;
- const shouldShowLoadingIndicator = isLoading || !hasImageDimensions;
-
- // Zoom view should be loaded only after measuring actual image dimensions, otherwise it causes blurred images on Android
return (
- {
- const layout = event.nativeEvent.layout;
- setContainerHeight(layout.height);
- }}
- >
- {Boolean(containerHeight) && (
- {
- onScaleChanged(scale);
- imageZoomScale.current = scale;
- }}
- >
-
-
- {/**
- Create an invisible view on top of the image so we can capture and set the amount of touches before
- the ImageZoom's PanResponder does. Children will be triggered first, so this needs to be inside the
- ImageZoom to work
- */}
-
-
- )}
- {shouldShowLoadingIndicator && }
-
+
);
}
diff --git a/src/components/ImageView/propTypes.js b/src/components/ImageView/propTypes.js
new file mode 100644
index 000000000000..3809d9aed043
--- /dev/null
+++ b/src/components/ImageView/propTypes.js
@@ -0,0 +1,46 @@
+import PropTypes from 'prop-types';
+
+const imageViewPropTypes = {
+ /** Whether source url requires authentication */
+ isAuthTokenRequired: PropTypes.bool,
+
+ /** Handles scale changed event in image zoom component. Used on native only */
+ // eslint-disable-next-line react/no-unused-prop-types
+ onScaleChanged: PropTypes.func.isRequired,
+
+ /** URL to full-sized image */
+ url: PropTypes.string.isRequired,
+
+ /** image file name */
+ fileName: PropTypes.string.isRequired,
+
+ /** Handles errors while displaying the image */
+ onError: PropTypes.func,
+
+ /** Whether this view is the active screen */
+ isFocused: PropTypes.bool,
+
+ /** Whether this AttachmentView is shown as part of a AttachmentCarousel */
+ isUsedInCarousel: PropTypes.bool,
+
+ /** When "isUsedInCarousel" is set to true, determines whether there is only one item in the carousel */
+ isSingleCarouselItem: PropTypes.bool,
+
+ /** The index of the carousel item */
+ carouselItemIndex: PropTypes.number,
+
+ /** The index of the currently active carousel item */
+ carouselActiveItemIndex: PropTypes.number,
+};
+
+const imageViewDefaultProps = {
+ isAuthTokenRequired: false,
+ onError: () => {},
+ isFocused: true,
+ isUsedInCarousel: false,
+ isSingleCarouselItem: false,
+ carouselItemIndex: 0,
+ carouselActiveItemIndex: 0,
+};
+
+export {imageViewPropTypes, imageViewDefaultProps};
diff --git a/src/components/ImageWithSizeCalculation.tsx b/src/components/ImageWithSizeCalculation.tsx
index 82b4b4759112..cffbc8941388 100644
--- a/src/components/ImageWithSizeCalculation.tsx
+++ b/src/components/ImageWithSizeCalculation.tsx
@@ -2,8 +2,8 @@ import delay from 'lodash/delay';
import React, {useEffect, useRef, useState} from 'react';
import {StyleProp, View, ViewStyle} from 'react-native';
import {OnLoadEvent} from 'react-native-fast-image';
+import useThemeStyles from '@hooks/useThemeStyles';
import Log from '@libs/Log';
-import useThemeStyles from '@styles/useThemeStyles';
import FullscreenLoadingIndicator from './FullscreenLoadingIndicator';
import Image from './Image';
import RESIZE_MODES from './Image/resizeModes';
diff --git a/src/components/Indicator.tsx b/src/components/Indicator.tsx
index 5332c4bd984f..7b7eae97fd86 100644
--- a/src/components/Indicator.tsx
+++ b/src/components/Indicator.tsx
@@ -2,10 +2,10 @@ import React from 'react';
import {StyleSheet, View} from 'react-native';
import {OnyxCollection, withOnyx} from 'react-native-onyx';
import {OnyxEntry} from 'react-native-onyx/lib/types';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as UserUtils from '@libs/UserUtils';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import * as PaymentMethods from '@userActions/PaymentMethods';
import ONYXKEYS from '@src/ONYXKEYS';
import type {BankAccountList, FundList, LoginList, Policy, PolicyMembers, ReimbursementAccount, UserWallet, WalletTerms} from '@src/types/onyx';
diff --git a/src/components/InlineCodeBlock/WrappedText.tsx b/src/components/InlineCodeBlock/WrappedText.tsx
index 6dbd17f18e2a..f0bb4e3ffd53 100644
--- a/src/components/InlineCodeBlock/WrappedText.tsx
+++ b/src/components/InlineCodeBlock/WrappedText.tsx
@@ -1,7 +1,7 @@
import React, {Fragment} from 'react';
import {StyleProp, TextStyle, View, ViewStyle} from 'react-native';
import Text from '@components/Text';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
import type ChildrenProps from '@src/types/utils/ChildrenProps';
diff --git a/src/components/InlineCodeBlock/index.native.tsx b/src/components/InlineCodeBlock/index.native.tsx
index 308b88e76e88..3a70308fa0cc 100644
--- a/src/components/InlineCodeBlock/index.native.tsx
+++ b/src/components/InlineCodeBlock/index.native.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import type {TText} from 'react-native-render-html';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import type InlineCodeBlockProps from './types';
import WrappedText from './WrappedText';
diff --git a/src/components/InlineSystemMessage.tsx b/src/components/InlineSystemMessage.tsx
index 6e6423a19a35..bef4c21289d5 100644
--- a/src/components/InlineSystemMessage.tsx
+++ b/src/components/InlineSystemMessage.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import {View} from 'react-native';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import Icon from './Icon';
import * as Expensicons from './Icon/Expensicons';
import Text from './Text';
diff --git a/src/components/InvertedFlatList/index.native.js b/src/components/InvertedFlatList/index.native.js
index d52d5a6d08f7..59810cba62c4 100644
--- a/src/components/InvertedFlatList/index.native.js
+++ b/src/components/InvertedFlatList/index.native.js
@@ -1,5 +1,5 @@
import React, {forwardRef} from 'react';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import BaseInvertedFlatList from './BaseInvertedFlatList';
import CellRendererComponent from './CellRendererComponent';
diff --git a/src/components/KYCWall/BaseKYCWall.js b/src/components/KYCWall/BaseKYCWall.js
index e229a860dd9f..5ad25d23f484 100644
--- a/src/components/KYCWall/BaseKYCWall.js
+++ b/src/components/KYCWall/BaseKYCWall.js
@@ -4,6 +4,7 @@ import {Dimensions} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import AddPaymentMethodMenu from '@components/AddPaymentMethodMenu';
+import * as BankAccounts from '@libs/actions/BankAccounts';
import getClickedTargetLocation from '@libs/getClickedTargetLocation';
import Log from '@libs/Log';
import Navigation from '@libs/Navigation/Navigation';
@@ -41,6 +42,7 @@ function KYCWall({
source,
userWallet,
walletTerms,
+ shouldShowPersonalBankAccountOption,
}) {
const anchorRef = useRef(null);
const transferBalanceButtonRef = useRef(null);
@@ -118,7 +120,7 @@ function KYCWall({
const selectPaymentMethod = (paymentMethod) => {
onSelectPaymentMethod(paymentMethod);
if (paymentMethod === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) {
- Navigation.navigate(addBankAccountRoute);
+ BankAccounts.openPersonalBankAccountSetupView();
} else if (paymentMethod === CONST.PAYMENT_METHODS.DEBIT_CARD) {
Navigation.navigate(addDebitCardRoute);
} else if (paymentMethod === CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT) {
@@ -212,6 +214,7 @@ function KYCWall({
setShouldShowAddPaymentMenu(false);
selectPaymentMethod(item);
}}
+ shouldShowPersonalBankAccountOption={shouldShowPersonalBankAccountOption}
/>
{children(continueAction, anchorRef)}
>
diff --git a/src/components/KYCWall/kycWallPropTypes.js b/src/components/KYCWall/kycWallPropTypes.js
index 58db2c1c1940..2f14f1e12e11 100644
--- a/src/components/KYCWall/kycWallPropTypes.js
+++ b/src/components/KYCWall/kycWallPropTypes.js
@@ -62,6 +62,9 @@ const propTypes = {
/** Callback for when a payment method has been selected */
onSelectPaymentMethod: PropTypes.func,
+
+ /** Whether the personal bank account option should be shown */
+ shouldShowPersonalBankAccountOption: PropTypes.bool,
};
const defaultProps = {
@@ -82,6 +85,7 @@ const defaultProps = {
},
shouldIncludeDebitCard: true,
onSelectPaymentMethod: () => {},
+ shouldShowPersonalBankAccountOption: false,
};
export {propTypes, defaultProps};
diff --git a/src/components/LHNOptionsList/LHNOptionsList.js b/src/components/LHNOptionsList/LHNOptionsList.js
index 8febd7f247d6..71b14b6fadcd 100644
--- a/src/components/LHNOptionsList/LHNOptionsList.js
+++ b/src/components/LHNOptionsList/LHNOptionsList.js
@@ -8,13 +8,13 @@ import _ from 'underscore';
import participantPropTypes from '@components/participantPropTypes';
import transactionPropTypes from '@components/transactionPropTypes';
import withCurrentReportID, {withCurrentReportIDDefaultProps, withCurrentReportIDPropTypes} from '@components/withCurrentReportID';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as ReportUtils from '@libs/ReportUtils';
import reportActionPropTypes from '@pages/home/report/reportActionPropTypes';
import reportPropTypes from '@pages/reportPropTypes';
import stylePropTypes from '@styles/stylePropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/components/LHNOptionsList/OptionRowLHN.js b/src/components/LHNOptionsList/OptionRowLHN.js
index 9aee141a9cde..6e244f55efec 100644
--- a/src/components/LHNOptionsList/OptionRowLHN.js
+++ b/src/components/LHNOptionsList/OptionRowLHN.js
@@ -15,6 +15,9 @@ import SubscriptAvatar from '@components/SubscriptAvatar';
import Text from '@components/Text';
import Tooltip from '@components/Tooltip';
import useLocalize from '@hooks/useLocalize';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import DateUtils from '@libs/DateUtils';
import DomUtils from '@libs/DomUtils';
@@ -24,9 +27,6 @@ import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManag
import * as ReportUtils from '@libs/ReportUtils';
import * as ContextMenuActions from '@pages/home/report/ContextMenu/ContextMenuActions';
import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
const propTypes = {
diff --git a/src/components/Lightbox.js b/src/components/Lightbox.js
new file mode 100644
index 000000000000..94cd1156bc7b
--- /dev/null
+++ b/src/components/Lightbox.js
@@ -0,0 +1,233 @@
+/* eslint-disable es/no-optional-chaining */
+import PropTypes from 'prop-types';
+import React, {useCallback, useEffect, useMemo, useState} from 'react';
+import {ActivityIndicator, PixelRatio, StyleSheet, View} from 'react-native';
+import useStyleUtils from '@hooks/useStyleUtils';
+import * as AttachmentsPropTypes from './Attachments/propTypes';
+import Image from './Image';
+import MultiGestureCanvas from './MultiGestureCanvas';
+import getCanvasFitScale from './MultiGestureCanvas/getCanvasFitScale';
+import {zoomRangeDefaultProps, zoomRangePropTypes} from './MultiGestureCanvas/propTypes';
+
+// Increase/decrease this number to change the number of concurrent lightboxes
+// The more concurrent lighboxes, the worse performance gets (especially on low-end devices)
+// -1 means unlimited
+const NUMBER_OF_CONCURRENT_LIGHTBOXES = 3;
+
+const cachedDimensions = new Map();
+
+/**
+ * On the native layer, we use a image library to handle zoom functionality
+ */
+const propTypes = {
+ ...zoomRangePropTypes,
+
+ /** Function for handle on press */
+ onPress: PropTypes.func,
+
+ /** Handles errors while displaying the image */
+ onError: PropTypes.func,
+
+ /** URL to full-sized attachment, SVG function, or numeric static image on native platforms */
+ source: AttachmentsPropTypes.attachmentSourcePropType.isRequired,
+
+ /** Whether source url requires authentication */
+ isAuthTokenRequired: PropTypes.bool,
+
+ /** Whether the Lightbox is used within a carousel component and there are other sibling elements */
+ hasSiblingCarouselItems: PropTypes.bool,
+
+ /** The index of the carousel item */
+ index: PropTypes.number,
+
+ /** The index of the currently active carousel item */
+ activeIndex: PropTypes.number,
+
+ /** Additional styles to add to the component */
+ style: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]),
+};
+
+const defaultProps = {
+ ...zoomRangeDefaultProps,
+
+ isAuthTokenRequired: false,
+ index: 0,
+ activeIndex: 0,
+ hasSiblingCarouselItems: false,
+ onPress: () => {},
+ onError: () => {},
+ style: {},
+};
+
+function Lightbox({isAuthTokenRequired, source, onScaleChanged, onPress, onError, style, index, activeIndex, hasSiblingCarouselItems, zoomRange}) {
+ const StyleUtils = useStyleUtils();
+
+ const [containerSize, setContainerSize] = useState({width: 0, height: 0});
+ const isContainerLoaded = containerSize.width !== 0 && containerSize.height !== 0;
+
+ const [imageDimensions, _setImageDimensions] = useState(() => cachedDimensions.get(source));
+ const setImageDimensions = (newDimensions) => {
+ _setImageDimensions(newDimensions);
+ cachedDimensions.set(source, newDimensions);
+ };
+
+ const isItemActive = index === activeIndex;
+ const [isActive, setActive] = useState(isItemActive);
+ const [isImageLoaded, setImageLoaded] = useState(false);
+
+ const isInactiveCarouselItem = hasSiblingCarouselItems && !isActive;
+ const [isFallbackVisible, setFallbackVisible] = useState(isInactiveCarouselItem);
+ const [isFallbackLoaded, setFallbackLoaded] = useState(false);
+
+ const isLightboxLoaded = imageDimensions?.lightboxSize != null;
+ const isLightboxInRange = useMemo(() => {
+ if (NUMBER_OF_CONCURRENT_LIGHTBOXES === -1) {
+ return true;
+ }
+
+ const indexCanvasOffset = Math.floor((NUMBER_OF_CONCURRENT_LIGHTBOXES - 1) / 2) || 0;
+ const indexOutOfRange = index > activeIndex + indexCanvasOffset || index < activeIndex - indexCanvasOffset;
+ return !indexOutOfRange;
+ }, [activeIndex, index]);
+ const isLightboxVisible = isLightboxInRange && (isActive || isLightboxLoaded || isFallbackLoaded);
+
+ const isLoading = isActive && (!isContainerLoaded || !isImageLoaded);
+
+ const updateCanvasSize = useCallback(
+ ({nativeEvent}) => setContainerSize({width: PixelRatio.roundToNearestPixel(nativeEvent.layout.width), height: PixelRatio.roundToNearestPixel(nativeEvent.layout.height)}),
+ [],
+ );
+
+ // We delay setting a page to active state by a (few) millisecond(s),
+ // to prevent the image transformer from flashing while still rendering
+ // Instead, we show the fallback image while the image transformer is loading the image
+ useEffect(() => {
+ if (isItemActive) {
+ setTimeout(() => setActive(true), 1);
+ } else {
+ setActive(false);
+ }
+ }, [isItemActive]);
+
+ useEffect(() => {
+ if (isLightboxVisible) {
+ return;
+ }
+ setImageLoaded(false);
+ }, [isLightboxVisible]);
+
+ useEffect(() => {
+ if (!hasSiblingCarouselItems) {
+ return;
+ }
+
+ if (isActive) {
+ if (isImageLoaded && isFallbackVisible) {
+ // We delay hiding the fallback image while image transformer is still rendering
+ setTimeout(() => {
+ setFallbackVisible(false);
+ setFallbackLoaded(false);
+ }, 100);
+ }
+ } else {
+ if (isLightboxVisible && isLightboxLoaded) {
+ return;
+ }
+
+ // Show fallback when the image goes out of focus or when the image is loading
+ setFallbackVisible(true);
+ }
+ }, [hasSiblingCarouselItems, isActive, isImageLoaded, isFallbackVisible, isLightboxLoaded, isLightboxVisible]);
+
+ const fallbackSize = useMemo(() => {
+ if (!hasSiblingCarouselItems || (imageDimensions?.lightboxSize == null && imageDimensions?.fallbackSize == null) || containerSize.width === 0 || containerSize.height === 0) {
+ return;
+ }
+
+ const imageSize = imageDimensions.lightboxSize || imageDimensions.fallbackSize;
+
+ const {minScale} = getCanvasFitScale({canvasSize: containerSize, contentSize: imageSize});
+
+ return {
+ width: PixelRatio.roundToNearestPixel(imageSize.width * minScale),
+ height: PixelRatio.roundToNearestPixel(imageSize.height * minScale),
+ };
+ }, [containerSize, hasSiblingCarouselItems, imageDimensions]);
+
+ return (
+
+ {isContainerLoaded && (
+ <>
+ {isLightboxVisible && (
+
+
+ setImageLoaded(true)}
+ onLoad={(e) => {
+ const width = (e.nativeEvent?.width || 0) / PixelRatio.get();
+ const height = (e.nativeEvent?.height || 0) / PixelRatio.get();
+ setImageDimensions({...imageDimensions, lightboxSize: {width, height}});
+ }}
+ />
+
+
+ )}
+
+ {/* Keep rendering the image without gestures as fallback if the carousel item is not active and while the lightbox is loading the image */}
+ {isFallbackVisible && (
+
+ setFallbackLoaded(true)}
+ onLoad={(e) => {
+ const width = e.nativeEvent?.width || 0;
+ const height = e.nativeEvent?.height || 0;
+
+ if (imageDimensions?.lightboxSize != null) {
+ return;
+ }
+
+ setImageDimensions({...imageDimensions, fallbackSize: {width, height}});
+ }}
+ />
+
+ )}
+
+ {/* Show activity indicator while the lightbox is still loading the image. */}
+ {isLoading && (
+
+ )}
+ >
+ )}
+
+ );
+}
+
+Lightbox.propTypes = propTypes;
+Lightbox.defaultProps = defaultProps;
+Lightbox.displayName = 'Lightbox';
+
+export default Lightbox;
diff --git a/src/components/LocaleContextProvider.tsx b/src/components/LocaleContextProvider.tsx
index 2fa4e1c749e6..2e68197bbaf0 100644
--- a/src/components/LocaleContextProvider.tsx
+++ b/src/components/LocaleContextProvider.tsx
@@ -36,7 +36,7 @@ type LocaleContextProps = {
datetimeToRelative: (datetime: string) => string;
/** Formats a datetime to local date and time string */
- datetimeToCalendarTime: (datetime: string, includeTimezone: boolean, isLowercase: boolean) => string;
+ datetimeToCalendarTime: (datetime: string, includeTimezone: boolean, isLowercase?: boolean) => string;
/** Updates date-fns internal locale */
updateLocale: () => void;
diff --git a/src/components/LocalePicker.tsx b/src/components/LocalePicker.tsx
index c04b0131744f..46adb1a4895e 100644
--- a/src/components/LocalePicker.tsx
+++ b/src/components/LocalePicker.tsx
@@ -1,8 +1,8 @@
import React from 'react';
import {OnyxEntry, withOnyx} from 'react-native-onyx';
import useLocalize from '@hooks/useLocalize';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as App from '@userActions/App';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/components/LocationErrorMessage/BaseLocationErrorMessage.js b/src/components/LocationErrorMessage/BaseLocationErrorMessage.js
index 5c0552e6bce7..14601c7c4ba2 100644
--- a/src/components/LocationErrorMessage/BaseLocationErrorMessage.js
+++ b/src/components/LocationErrorMessage/BaseLocationErrorMessage.js
@@ -8,9 +8,9 @@ import Text from '@components/Text';
import TextLink from '@components/TextLink';
import Tooltip from '@components/Tooltip';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
-import colors from '@styles/colors';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
+import colors from '@styles/theme/colors';
import CONST from '@src/CONST';
import * as locationErrorMessagePropTypes from './locationErrorMessagePropTypes';
diff --git a/src/components/Lottie/Lottie.tsx b/src/components/Lottie/Lottie.tsx
index 868eaf8cb95e..2bf7df056c4d 100644
--- a/src/components/Lottie/Lottie.tsx
+++ b/src/components/Lottie/Lottie.tsx
@@ -3,7 +3,7 @@ import React, {ForwardedRef, forwardRef} from 'react';
import {View} from 'react-native';
import DotLottieAnimation from '@components/LottieAnimations/types';
import useNetwork from '@hooks/useNetwork';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
type Props = {
source: DotLottieAnimation;
diff --git a/src/components/MagicCodeInput.js b/src/components/MagicCodeInput.js
index 91430602a115..55a65237a691 100644
--- a/src/components/MagicCodeInput.js
+++ b/src/components/MagicCodeInput.js
@@ -4,10 +4,10 @@ import {StyleSheet, View} from 'react-native';
import {TapGestureHandler} from 'react-native-gesture-handler';
import _ from 'underscore';
import useNetwork from '@hooks/useNetwork';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as Browser from '@libs/Browser';
import * as ValidationUtils from '@libs/ValidationUtils';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import FormHelpMessage from './FormHelpMessage';
import networkPropTypes from './networkPropTypes';
diff --git a/src/components/MapView/Direction.tsx b/src/components/MapView/Direction.tsx
index ca4b26f2f2db..b3162149a48d 100644
--- a/src/components/MapView/Direction.tsx
+++ b/src/components/MapView/Direction.tsx
@@ -1,5 +1,5 @@
import Mapbox from '@rnmapbox/maps';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import {DirectionProps} from './MapViewTypes';
function Direction({coordinates}: DirectionProps) {
diff --git a/src/components/MapView/Direction.website.tsx b/src/components/MapView/Direction.website.tsx
index 9e5af2aa2b1d..f85bda125473 100644
--- a/src/components/MapView/Direction.website.tsx
+++ b/src/components/MapView/Direction.website.tsx
@@ -5,7 +5,7 @@
import React from 'react';
import {Layer, Source} from 'react-map-gl';
import {View} from 'react-native';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import {DirectionProps} from './MapViewTypes';
function Direction({coordinates}: DirectionProps) {
diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx
index 91f9d9930079..7b3d73479dde 100644
--- a/src/components/MapView/MapView.tsx
+++ b/src/components/MapView/MapView.tsx
@@ -3,10 +3,10 @@ import Mapbox, {MapState, MarkerView, setAccessToken} from '@rnmapbox/maps';
import {forwardRef, memo, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
+import useThemeStyles from '@hooks/useThemeStyles';
import setUserLocation from '@libs/actions/UserLocation';
import compose from '@libs/compose';
import getCurrentPosition from '@libs/getCurrentPosition';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import useLocalize from '@src/hooks/useLocalize';
import useNetwork from '@src/hooks/useNetwork';
diff --git a/src/components/MapView/MapView.website.tsx b/src/components/MapView/MapView.website.tsx
index ecdb62efbe15..7910d7f93a29 100644
--- a/src/components/MapView/MapView.website.tsx
+++ b/src/components/MapView/MapView.website.tsx
@@ -9,9 +9,9 @@ import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useState
import Map, {MapRef, Marker} from 'react-map-gl';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import setUserLocation from '@userActions/UserLocation';
import CONST from '@src/CONST';
import useLocalize from '@src/hooks/useLocalize';
diff --git a/src/components/MapView/PendingMapView.tsx b/src/components/MapView/PendingMapView.tsx
index 2acdb59d3782..0af816785e9a 100644
--- a/src/components/MapView/PendingMapView.tsx
+++ b/src/components/MapView/PendingMapView.tsx
@@ -4,7 +4,7 @@ import {View} from 'react-native';
import BlockingView from '@components/BlockingViews/BlockingView';
import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
import {PendingMapViewProps} from './MapViewTypes';
diff --git a/src/components/MentionSuggestions.tsx b/src/components/MentionSuggestions.tsx
index b42540bacb13..f1daaaa229be 100644
--- a/src/components/MentionSuggestions.tsx
+++ b/src/components/MentionSuggestions.tsx
@@ -1,9 +1,9 @@
import React, {useCallback} from 'react';
import {View} from 'react-native';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import getStyledTextArray from '@libs/GetStyledTextArray';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import {Icon} from '@src/types/onyx/OnyxCommon';
import AutoCompleteSuggestions from './AutoCompleteSuggestions';
diff --git a/src/components/MenuItem.js b/src/components/MenuItem.js
index a56729f630d5..b1f6b7f7319a 100644
--- a/src/components/MenuItem.js
+++ b/src/components/MenuItem.js
@@ -2,20 +2,21 @@ import ExpensiMark from 'expensify-common/lib/ExpensiMark';
import React, {useEffect, useMemo} from 'react';
import {View} from 'react-native';
import _ from 'underscore';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import ControlSelection from '@libs/ControlSelection';
import convertToLTR from '@libs/convertToLTR';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import getButtonState from '@libs/getButtonState';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import * as Session from '@userActions/Session';
import CONST from '@src/CONST';
import Avatar from './Avatar';
import Badge from './Badge';
import DisplayNames from './DisplayNames';
+import FormHelpMessage from './FormHelpMessage';
import Hoverable from './Hoverable';
import Icon from './Icon';
import * as Expensicons from './Icon/Expensicons';
@@ -106,6 +107,7 @@ const MenuItem = React.forwardRef((props, ref) => {
props.interactive && props.disabled ? {...styles.userSelectNone} : undefined,
styles.ltr,
isDeleted ? styles.offlineFeedback.deleted : undefined,
+ props.titleTextStyle,
],
props.titleStyle,
);
@@ -180,6 +182,8 @@ const MenuItem = React.forwardRef((props, ref) => {
onPressOut={ControlSelection.unblock}
onSecondaryInteraction={props.onSecondaryInteraction}
style={({pressed}) => [
+ props.containerStyle,
+ props.errorText ? styles.pb5 : {},
style,
!props.interactive && styles.cursorDefault,
StyleUtils.getButtonBackgroundColorStyle(getButtonState(props.focused || isHovered, pressed, props.success, props.disabled, props.interactive), true),
@@ -385,6 +389,14 @@ const MenuItem = React.forwardRef((props, ref) => {
{props.shouldShowRightComponent && props.rightComponent}
{props.shouldShowSelectedState && }
+ {Boolean(props.errorText) && (
+
+ )}
>
)}
diff --git a/src/components/MessagesRow.tsx b/src/components/MessagesRow.tsx
index c0b0a7807deb..e019bd1aef30 100644
--- a/src/components/MessagesRow.tsx
+++ b/src/components/MessagesRow.tsx
@@ -1,8 +1,8 @@
import React from 'react';
import {StyleProp, View, ViewStyle} from 'react-native';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as Localize from '@libs/Localize';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import DotIndicatorMessage from './DotIndicatorMessage';
diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx
index 1ea284b55280..89640c56f5ef 100644
--- a/src/components/Modal/BaseModal.tsx
+++ b/src/components/Modal/BaseModal.tsx
@@ -4,12 +4,12 @@ import ReactNativeModal from 'react-native-modal';
import ColorSchemeWrapper from '@components/ColorSchemeWrapper';
import usePrevious from '@hooks/usePrevious';
import useSafeAreaInsets from '@hooks/useSafeAreaInsets';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import ComposerFocusManager from '@libs/ComposerFocusManager';
import useNativeDriver from '@libs/useNativeDriver';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import * as Modal from '@userActions/Modal';
import CONST from '@src/CONST';
diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx
index bfc899f9c278..4269420dcd7f 100644
--- a/src/components/Modal/index.tsx
+++ b/src/components/Modal/index.tsx
@@ -1,8 +1,8 @@
import React, {useState} from 'react';
import withWindowDimensions from '@components/withWindowDimensions';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
import StatusBar from '@libs/StatusBar';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
import CONST from '@src/CONST';
import BaseModal from './BaseModal';
import BaseModalProps from './types';
diff --git a/src/components/MoneyReportHeader.js b/src/components/MoneyReportHeader.js
index f190310b67d2..3e6ce7e5be52 100644
--- a/src/components/MoneyReportHeader.js
+++ b/src/components/MoneyReportHeader.js
@@ -7,6 +7,7 @@ import _ from 'underscore';
import GoogleMeetIcon from '@assets/images/google-meet.svg';
import ZoomIcon from '@assets/images/zoom-icon.svg';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import compose from '@libs/compose';
import * as CurrencyUtils from '@libs/CurrencyUtils';
@@ -16,7 +17,6 @@ import * as ReportUtils from '@libs/ReportUtils';
import iouReportPropTypes from '@pages/iouReportPropTypes';
import nextStepPropTypes from '@pages/nextStepPropTypes';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import * as Link from '@userActions/Link';
import * as Session from '@userActions/Session';
diff --git a/src/components/MoneyReportHeaderStatusBar.js b/src/components/MoneyReportHeaderStatusBar.js
index e693ce5e502b..687bc92ffbcd 100644
--- a/src/components/MoneyReportHeaderStatusBar.js
+++ b/src/components/MoneyReportHeaderStatusBar.js
@@ -1,9 +1,9 @@
import React, {useMemo} from 'react';
import {Text, View} from 'react-native';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as NextStepUtils from '@libs/NextStepUtils';
import nextStepPropTypes from '@pages/nextStepPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import RenderHTML from './RenderHTML';
diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js
index 1bd7b71c5aa3..fc3e4b095873 100755
--- a/src/components/MoneyRequestConfirmationList.js
+++ b/src/components/MoneyRequestConfirmationList.js
@@ -9,6 +9,8 @@ import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import useLocalize from '@hooks/useLocalize';
import usePermissions from '@hooks/usePermissions';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import * as CurrencyUtils from '@libs/CurrencyUtils';
import DistanceRequestUtils from '@libs/DistanceRequestUtils';
@@ -22,8 +24,6 @@ import * as ReceiptUtils from '@libs/ReceiptUtils';
import * as ReportUtils from '@libs/ReportUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
import {iouDefaultProps, iouPropTypes} from '@pages/iou/propTypes';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
@@ -521,6 +521,7 @@ function MoneyRequestConfirmationList(props) {
horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT,
vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM,
}}
+ shouldShowPersonalBankAccountOption
/>
) : (
{scaleX: number; scaleY: number; minScale: number; maxScale: number};
+
+const getCanvasFitScale: GetCanvasFitScale = ({canvasSize, contentSize}) => {
+ const scaleX = canvasSize.width / contentSize.width;
+ const scaleY = canvasSize.height / contentSize.height;
+
+ const minScale = Math.min(scaleX, scaleY);
+ const maxScale = Math.max(scaleX, scaleY);
+
+ return {scaleX, scaleY, minScale, maxScale};
+};
+
+export default getCanvasFitScale;
diff --git a/src/components/Attachments/AttachmentCarousel/Pager/ImageTransformer.js b/src/components/MultiGestureCanvas/index.js
similarity index 71%
rename from src/components/Attachments/AttachmentCarousel/Pager/ImageTransformer.js
rename to src/components/MultiGestureCanvas/index.js
index cc1e20cb44e0..c5fd2632c22d 100644
--- a/src/components/Attachments/AttachmentCarousel/Pager/ImageTransformer.js
+++ b/src/components/MultiGestureCanvas/index.js
@@ -1,5 +1,3 @@
-/* eslint-disable es/no-optional-chaining */
-import PropTypes from 'prop-types';
import React, {useContext, useEffect, useMemo, useRef, useState} from 'react';
import {View} from 'react-native';
import {Gesture, GestureDetector} from 'react-native-gesture-handler';
@@ -15,18 +13,19 @@ import Animated, {
withDecay,
withSpring,
} from 'react-native-reanimated';
-import useThemeStyles from '@styles/useThemeStyles';
-import AttachmentCarouselPagerContext from './AttachmentCarouselPagerContext';
-import ImageWrapper from './ImageWrapper';
-
-const MIN_ZOOM_SCALE_WITHOUT_BOUNCE = 1;
-const MAX_ZOOM_SCALE_WITHOUT_BOUNCE = 20;
-
-const MIN_ZOOM_SCALE_WITH_BOUNCE = MIN_ZOOM_SCALE_WITHOUT_BOUNCE * 0.7;
-const MAX_ZOOM_SCALE_WITH_BOUNCE = MAX_ZOOM_SCALE_WITHOUT_BOUNCE * 1.5;
+import AttachmentCarouselPagerContext from '@components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPagerContext';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
+import getCanvasFitScale from './getCanvasFitScale';
+import {defaultZoomRange, multiGestureCanvasDefaultProps, multiGestureCanvasPropTypes} from './propTypes';
const DOUBLE_TAP_SCALE = 3;
+const zoomScaleBounceFactors = {
+ min: 0.7,
+ max: 1.5,
+};
+
const SPRING_CONFIG = {
mass: 1,
stiffness: 1000,
@@ -39,44 +38,54 @@ function clamp(value, lowerBound, upperBound) {
return Math.min(Math.max(lowerBound, value), upperBound);
}
-const imageTransformerPropTypes = {
- imageWidth: PropTypes.number,
- imageHeight: PropTypes.number,
- imageScaleX: PropTypes.number,
- imageScaleY: PropTypes.number,
- scaledImageWidth: PropTypes.number,
- scaledImageHeight: PropTypes.number,
- isActive: PropTypes.bool.isRequired,
- children: PropTypes.node.isRequired,
-};
+function getDeepDefaultProps({contentSize: contentSizeProp = {}, zoomRange: zoomRangeProp = {}}) {
+ const contentSize = {
+ width: contentSizeProp.width == null ? 1 : contentSizeProp.width,
+ height: contentSizeProp.height == null ? 1 : contentSizeProp.height,
+ };
-const imageTransformerDefaultProps = {
- imageWidth: 0,
- imageHeight: 0,
- imageScaleX: 1,
- imageScaleY: 1,
- scaledImageWidth: 0,
- scaledImageHeight: 0,
-};
+ const zoomRange = {
+ min: zoomRangeProp.min == null ? defaultZoomRange.min : zoomRangeProp.min,
+ max: zoomRangeProp.max == null ? defaultZoomRange.max : zoomRangeProp.max,
+ };
-function ImageTransformer({imageWidth, imageHeight, imageScaleX, imageScaleY, scaledImageWidth, scaledImageHeight, isActive, children}) {
+ return {contentSize, zoomRange};
+}
+
+function MultiGestureCanvas({canvasSize, isActive = true, onScaleChanged, children, ...props}) {
const styles = useThemeStyles();
- const {canvasWidth, canvasHeight, onTap, onSwipe, onSwipeSuccess, pagerRef, shouldPagerScroll, isScrolling, onPinchGestureChange} = useContext(AttachmentCarouselPagerContext);
+ const StyleUtils = useStyleUtils();
+ const {contentSize, zoomRange} = getDeepDefaultProps(props);
+
+ const attachmentCarouselPagerContext = useContext(AttachmentCarouselPagerContext);
+
+ const pagerRefFallback = useRef(null);
+ const {onTap, onSwipe, onSwipeSuccess, pagerRef, shouldPagerScroll, isScrolling, onPinchGestureChange} = attachmentCarouselPagerContext || {
+ onTap: () => undefined,
+ onSwipe: () => undefined,
+ onSwipeSuccess: () => undefined,
+ onPinchGestureChange: () => undefined,
+ pagerRef: pagerRefFallback,
+ shouldPagerScroll: false,
+ isScrolling: false,
+ ...props,
+ };
- const minImageScale = useMemo(() => Math.min(imageScaleX, imageScaleY), [imageScaleX, imageScaleY]);
- const maxImageScale = useMemo(() => Math.max(imageScaleX, imageScaleY), [imageScaleX, imageScaleY]);
+ const {minScale: minContentScale, maxScale: maxContentScale} = useMemo(() => getCanvasFitScale({canvasSize, contentSize}), [canvasSize, contentSize]);
+ const scaledWidth = useMemo(() => contentSize.width * minContentScale, [contentSize.width, minContentScale]);
+ const scaledHeight = useMemo(() => contentSize.height * minContentScale, [contentSize.height, minContentScale]);
// On double tap zoom to fill, but at least 3x zoom
- const doubleTapScale = useMemo(() => Math.max(maxImageScale / minImageScale, DOUBLE_TAP_SCALE), [maxImageScale, minImageScale]);
+ const doubleTapScale = useMemo(() => Math.max(DOUBLE_TAP_SCALE, maxContentScale / minContentScale), [maxContentScale, minContentScale]);
const zoomScale = useSharedValue(1);
- // Adding together the pinch zoom scale and the initial scale to fit the image into the canvas
- // Using the smaller imageScale, so that the immage is not bigger than the canvas
+ // Adding together the pinch zoom scale and the initial scale to fit the content into the canvas
+ // Using the smaller content scale, so that the immage is not bigger than the canvas
// and not smaller than needed to fit
- const totalScale = useDerivedValue(() => zoomScale.value * minImageScale, [minImageScale]);
+ const totalScale = useDerivedValue(() => zoomScale.value * minContentScale, [minContentScale]);
- const zoomScaledImageWidth = useDerivedValue(() => imageWidth * totalScale.value, [imageWidth]);
- const zoomScaledImageHeight = useDerivedValue(() => imageHeight * totalScale.value, [imageHeight]);
+ const zoomScaledContentWidth = useDerivedValue(() => contentSize.width * totalScale.value, [contentSize.width]);
+ const zoomScaledContentHeight = useDerivedValue(() => contentSize.height * totalScale.value, [contentSize.height]);
// used for pan gesture
const translateY = useSharedValue(0);
@@ -104,22 +113,22 @@ function ImageTransformer({imageWidth, imageHeight, imageScaleX, imageScaleY, sc
// store scale in between gestures
const pinchScaleOffset = useSharedValue(1);
- // disable pan vertically when image is smaller than screen
- const canPanVertically = useDerivedValue(() => canvasHeight < zoomScaledImageHeight.value, [canvasHeight]);
+ // disable pan vertically when content is smaller than screen
+ const canPanVertically = useDerivedValue(() => canvasSize.height < zoomScaledContentHeight.value, [canvasSize.height]);
- // calculates bounds of the scaled image
+ // calculates bounds of the scaled content
// can we pan left/right/up/down
// can be used to limit gesture or implementing tension effect
const getBounds = useWorkletCallback(() => {
let rightBoundary = 0;
let topBoundary = 0;
- if (canvasWidth < zoomScaledImageWidth.value) {
- rightBoundary = Math.abs(canvasWidth - zoomScaledImageWidth.value) / 2;
+ if (canvasSize.width < zoomScaledContentWidth.value) {
+ rightBoundary = Math.abs(canvasSize.width - zoomScaledContentWidth.value) / 2;
}
- if (canvasHeight < zoomScaledImageHeight.value) {
- topBoundary = Math.abs(zoomScaledImageHeight.value - canvasHeight) / 2;
+ if (canvasSize.height < zoomScaledContentHeight.value) {
+ topBoundary = Math.abs(zoomScaledContentHeight.value - canvasSize.height) / 2;
}
const maxVector = {x: rightBoundary, y: topBoundary};
@@ -142,7 +151,7 @@ function ImageTransformer({imageWidth, imageHeight, imageScaleX, imageScaleY, sc
canPanLeft: target.x < maxVector.x,
canPanRight: target.x > minVector.x,
};
- }, [canvasWidth, canvasHeight]);
+ }, [canvasSize.width, canvasSize.height]);
const afterPanGesture = useWorkletCallback(() => {
const {target, isInBoundaryX, isInBoundaryY, minVector, maxVector} = getBounds();
@@ -166,7 +175,7 @@ function ImageTransformer({imageWidth, imageHeight, imageScaleX, imageScaleY, sc
const deceleration = 0.9915;
if (isInBoundaryX) {
- if (Math.abs(panVelocityX.value) > 0 && zoomScale.value <= MAX_ZOOM_SCALE_WITHOUT_BOUNCE) {
+ if (Math.abs(panVelocityX.value) > 0 && zoomScale.value <= zoomRange.max) {
offsetX.value = withDecay({
velocity: panVelocityX.value,
clamp: [minVector.x, maxVector.x],
@@ -181,8 +190,8 @@ function ImageTransformer({imageWidth, imageHeight, imageScaleX, imageScaleY, sc
if (isInBoundaryY) {
if (
Math.abs(panVelocityY.value) > 0 &&
- zoomScale.value <= MAX_ZOOM_SCALE_WITHOUT_BOUNCE &&
- // limit vertical pan only when image is smaller than screen
+ zoomScale.value <= zoomRange.max &&
+ // limit vertical pan only when content is smaller than screen
offsetY.value !== minVector.y &&
offsetY.value !== maxVector.y
) {
@@ -210,42 +219,42 @@ function ImageTransformer({imageWidth, imageHeight, imageScaleX, imageScaleY, sc
stopAnimation();
- const canvasOffsetX = Math.max(0, (canvasWidth - scaledImageWidth) / 2);
- const canvasOffsetY = Math.max(0, (canvasHeight - scaledImageHeight) / 2);
+ const canvasOffsetX = Math.max(0, (canvasSize.width - scaledWidth) / 2);
+ const canvasOffsetY = Math.max(0, (canvasSize.height - scaledHeight) / 2);
- const imageFocal = {
- x: clamp(canvasFocalX - canvasOffsetX, 0, scaledImageWidth),
- y: clamp(canvasFocalY - canvasOffsetY, 0, scaledImageHeight),
+ const contentFocal = {
+ x: clamp(canvasFocalX - canvasOffsetX, 0, scaledWidth),
+ y: clamp(canvasFocalY - canvasOffsetY, 0, scaledHeight),
};
const canvasCenter = {
- x: canvasWidth / 2,
- y: canvasHeight / 2,
+ x: canvasSize.width / 2,
+ y: canvasSize.height / 2,
};
- const originImageCenter = {
- x: scaledImageWidth / 2,
- y: scaledImageHeight / 2,
+ const originContentCenter = {
+ x: scaledWidth / 2,
+ y: scaledHeight / 2,
};
- const targetImageSize = {
- width: scaledImageWidth * doubleTapScale,
- height: scaledImageHeight * doubleTapScale,
+ const targetContentSize = {
+ width: scaledWidth * doubleTapScale,
+ height: scaledHeight * doubleTapScale,
};
- const targetImageCenter = {
- x: targetImageSize.width / 2,
- y: targetImageSize.height / 2,
+ const targetContentCenter = {
+ x: targetContentSize.width / 2,
+ y: targetContentSize.height / 2,
};
const currentOrigin = {
- x: (targetImageCenter.x - canvasCenter.x) * -1,
- y: (targetImageCenter.y - canvasCenter.y) * -1,
+ x: (targetContentCenter.x - canvasCenter.x) * -1,
+ y: (targetContentCenter.y - canvasCenter.y) * -1,
};
const koef = {
- x: (1 / originImageCenter.x) * imageFocal.x - 1,
- y: (1 / originImageCenter.y) * imageFocal.y - 1,
+ x: (1 / originContentCenter.x) * contentFocal.x - 1,
+ y: (1 / originContentCenter.y) * contentFocal.y - 1,
};
const target = {
@@ -253,7 +262,7 @@ function ImageTransformer({imageWidth, imageHeight, imageScaleX, imageScaleY, sc
y: currentOrigin.y * koef.y,
};
- if (targetImageSize.height < canvasHeight) {
+ if (targetContentSize.height < canvasSize.height) {
target.y = 0;
}
@@ -262,7 +271,7 @@ function ImageTransformer({imageWidth, imageHeight, imageScaleX, imageScaleY, sc
zoomScale.value = withSpring(doubleTapScale, SPRING_CONFIG);
pinchScaleOffset.value = doubleTapScale;
},
- [scaledImageWidth, scaledImageHeight, canvasWidth, canvasHeight],
+ [scaledWidth, scaledHeight, canvasSize, doubleTapScale],
);
const reset = useWorkletCallback((animated) => {
@@ -295,6 +304,10 @@ function ImageTransformer({imageWidth, imageHeight, imageScaleX, imageScaleY, sc
} else {
zoomToCoordinates(evt.x, evt.y);
}
+
+ if (onScaleChanged != null) {
+ runOnJS(onScaleChanged)(zoomScale.value);
+ }
});
const panGestureRef = useRef(Gesture.Pan());
@@ -396,7 +409,7 @@ function ImageTransformer({imageWidth, imageHeight, imageScaleX, imageScaleY, sc
};
offsetY.value = withSpring(
- maybeInvert(imageHeight * 2),
+ maybeInvert(contentSize.height * 2),
{
stiffness: 50,
damping: 30,
@@ -423,10 +436,10 @@ function ImageTransformer({imageWidth, imageHeight, imageScaleX, imageScaleY, sc
const getAdjustedFocal = useWorkletCallback(
(focalX, focalY) => ({
- x: focalX - (canvasWidth / 2 + offsetX.value),
- y: focalY - (canvasHeight / 2 + offsetY.value),
+ x: focalX - (canvasSize.width / 2 + offsetX.value),
+ y: focalY - (canvasSize.height / 2 + offsetY.value),
}),
- [canvasWidth, canvasHeight],
+ [canvasSize.width, canvasSize.height],
);
// used to store event scale value when we limit scale
@@ -455,7 +468,7 @@ function ImageTransformer({imageWidth, imageHeight, imageScaleX, imageScaleY, sc
.onChange((evt) => {
const newZoomScale = pinchScaleOffset.value * evt.scale;
- if (zoomScale.value >= MIN_ZOOM_SCALE_WITH_BOUNCE && zoomScale.value <= MAX_ZOOM_SCALE_WITH_BOUNCE) {
+ if (zoomScale.value >= zoomRange.min * zoomScaleBounceFactors.min && zoomScale.value <= zoomRange.max * zoomScaleBounceFactors.max) {
zoomScale.value = newZoomScale;
pinchGestureScale.value = evt.scale;
}
@@ -464,7 +477,7 @@ function ImageTransformer({imageWidth, imageHeight, imageScaleX, imageScaleY, sc
const newPinchTranslateX = adjustedFocal.x + pinchGestureScale.value * origin.x.value * -1;
const newPinchTranslateY = adjustedFocal.y + pinchGestureScale.value * origin.y.value * -1;
- if (zoomScale.value >= MIN_ZOOM_SCALE_WITHOUT_BOUNCE && zoomScale.value <= MAX_ZOOM_SCALE_WITHOUT_BOUNCE) {
+ if (zoomScale.value >= zoomRange.min && zoomScale.value <= zoomRange.max) {
pinchTranslateX.value = newPinchTranslateX;
pinchTranslateY.value = newPinchTranslateY;
} else {
@@ -480,12 +493,12 @@ function ImageTransformer({imageWidth, imageHeight, imageScaleX, imageScaleY, sc
pinchScaleOffset.value = zoomScale.value;
pinchGestureScale.value = 1;
- if (pinchScaleOffset.value < MIN_ZOOM_SCALE_WITHOUT_BOUNCE) {
- pinchScaleOffset.value = MIN_ZOOM_SCALE_WITHOUT_BOUNCE;
- zoomScale.value = withSpring(MIN_ZOOM_SCALE_WITHOUT_BOUNCE, SPRING_CONFIG);
- } else if (pinchScaleOffset.value > MAX_ZOOM_SCALE_WITHOUT_BOUNCE) {
- pinchScaleOffset.value = MAX_ZOOM_SCALE_WITHOUT_BOUNCE;
- zoomScale.value = withSpring(MAX_ZOOM_SCALE_WITHOUT_BOUNCE, SPRING_CONFIG);
+ if (pinchScaleOffset.value < zoomRange.min) {
+ pinchScaleOffset.value = zoomRange.min;
+ zoomScale.value = withSpring(zoomRange.min, SPRING_CONFIG);
+ } else if (pinchScaleOffset.value > zoomRange.max) {
+ pinchScaleOffset.value = zoomRange.max;
+ zoomScale.value = withSpring(zoomRange.max, SPRING_CONFIG);
}
if (pinchBounceTranslateX.value !== 0 || pinchBounceTranslateY.value !== 0) {
@@ -494,6 +507,10 @@ function ImageTransformer({imageWidth, imageHeight, imageScaleX, imageScaleY, sc
}
pinchGestureRunning.value = false;
+
+ if (onScaleChanged != null) {
+ runOnJS(onScaleChanged)(zoomScale.value);
+ }
});
const [isPinchGestureInUse, setIsPinchGestureInUse] = useState(false);
@@ -556,25 +573,30 @@ function ImageTransformer({imageWidth, imageHeight, imageScaleX, imageScaleY, sc
style={[
styles.flex1,
{
- width: canvasWidth,
+ width: canvasSize.width,
+ overflow: 'hidden',
},
]}
>
-
+
{children}
-
+
);
}
-ImageTransformer.propTypes = imageTransformerPropTypes;
-ImageTransformer.defaultProps = imageTransformerDefaultProps;
-ImageTransformer.displayName = 'ImageTransformer';
+MultiGestureCanvas.propTypes = multiGestureCanvasPropTypes;
+MultiGestureCanvas.defaultProps = multiGestureCanvasDefaultProps;
+MultiGestureCanvas.displayName = 'MultiGestureCanvas';
-export default ImageTransformer;
+export default MultiGestureCanvas;
+export {defaultZoomRange, zoomScaleBounceFactors};
diff --git a/src/components/MultiGestureCanvas/propTypes.js b/src/components/MultiGestureCanvas/propTypes.js
new file mode 100644
index 000000000000..f1961ec0e156
--- /dev/null
+++ b/src/components/MultiGestureCanvas/propTypes.js
@@ -0,0 +1,73 @@
+import PropTypes from 'prop-types';
+
+const defaultZoomRange = {
+ min: 1,
+ max: 20,
+};
+
+const zoomRangePropTypes = {
+ /** Range of zoom that can be applied to the content by pinching or double tapping. */
+ zoomRange: PropTypes.shape({
+ min: PropTypes.number,
+ max: PropTypes.number,
+ }),
+};
+
+const zoomRangeDefaultProps = {
+ zoomRange: {
+ min: defaultZoomRange.min,
+ max: defaultZoomRange.max,
+ },
+};
+
+const multiGestureCanvasPropTypes = {
+ ...zoomRangePropTypes,
+
+ /**
+ * Wheter the canvas is currently active (in the screen) or not.
+ * Disables certain gestures and functionality
+ */
+ isActive: PropTypes.bool,
+
+ /** Handles scale changed event */
+ onScaleChanged: PropTypes.func,
+
+ /** The width and height of the canvas.
+ * This is needed in order to properly scale the content in the canvas
+ */
+ canvasSize: PropTypes.shape({
+ width: PropTypes.number.isRequired,
+ height: PropTypes.number.isRequired,
+ }).isRequired,
+
+ /** The width and height of the content.
+ * This is needed in order to properly scale the content in the canvas
+ */
+ contentSize: PropTypes.shape({
+ width: PropTypes.number,
+ height: PropTypes.number,
+ }),
+
+ /** The scale factors (scaleX, scaleY) that are used to scale the content (width/height) to the canvas size.
+ * `scaledWidth` and `scaledHeight` reflect the actual size of the content after scaling.
+ */
+ contentScaling: PropTypes.shape({
+ scaleX: PropTypes.number,
+ scaleY: PropTypes.number,
+ scaledWidth: PropTypes.number,
+ scaledHeight: PropTypes.number,
+ }),
+
+ /** Content that should be transformed inside the canvas (images, pdf, ...) */
+ children: PropTypes.node.isRequired,
+};
+
+const multiGestureCanvasDefaultProps = {
+ isActive: true,
+ onScaleChanged: () => undefined,
+ contentSize: undefined,
+ contentScaling: undefined,
+ zoomRange: undefined,
+};
+
+export {defaultZoomRange, zoomRangePropTypes, zoomRangeDefaultProps, multiGestureCanvasPropTypes, multiGestureCanvasDefaultProps};
diff --git a/src/components/MultipleAvatars.tsx b/src/components/MultipleAvatars.tsx
index a7b22a663e08..997e63cb022f 100644
--- a/src/components/MultipleAvatars.tsx
+++ b/src/components/MultipleAvatars.tsx
@@ -1,10 +1,10 @@
import React, {memo, useMemo} from 'react';
import {StyleProp, View, ViewStyle} from 'react-native';
import {ValueOf} from 'type-fest';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import {AvatarSource} from '@libs/UserUtils';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import type {Icon} from '@src/types/onyx/OnyxCommon';
diff --git a/src/components/OfflineIndicator.tsx b/src/components/OfflineIndicator.tsx
index b402caa2c750..739befa7d081 100644
--- a/src/components/OfflineIndicator.tsx
+++ b/src/components/OfflineIndicator.tsx
@@ -2,8 +2,8 @@ import React, {useMemo} from 'react';
import {StyleProp, View, ViewStyle} from 'react-native';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import Icon from './Icon';
import * as Expensicons from './Icon/Expensicons';
diff --git a/src/components/OfflineWithFeedback.tsx b/src/components/OfflineWithFeedback.tsx
index 78c93c250f84..5fcf1fe7442b 100644
--- a/src/components/OfflineWithFeedback.tsx
+++ b/src/components/OfflineWithFeedback.tsx
@@ -1,9 +1,9 @@
import React, {useCallback} from 'react';
import {ImageStyle, StyleProp, TextStyle, View, ViewStyle} from 'react-native';
import useNetwork from '@hooks/useNetwork';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import shouldRenderOffscreen from '@libs/shouldRenderOffscreen';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import * as OnyxCommon from '@src/types/onyx/OnyxCommon';
import ChildrenProps from '@src/types/utils/ChildrenProps';
diff --git a/src/components/Onfido/BaseOnfidoWeb.js b/src/components/Onfido/BaseOnfidoWeb.js
index 7a3b90945a5c..09ec96cf5b1e 100644
--- a/src/components/Onfido/BaseOnfidoWeb.js
+++ b/src/components/Onfido/BaseOnfidoWeb.js
@@ -3,10 +3,10 @@ import * as OnfidoSDK from 'onfido-sdk-ui';
import React, {forwardRef, useEffect} from 'react';
import _ from 'underscore';
import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
import Log from '@libs/Log';
-import fontFamily from '@styles/fontFamily';
-import fontWeightBold from '@styles/fontWeight/bold';
-import useTheme from '@styles/themes/useTheme';
+import fontFamily from '@styles/utils/fontFamily';
+import fontWeightBold from '@styles/utils/fontWeight/bold';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import './index.css';
diff --git a/src/components/OptionRow.js b/src/components/OptionRow.js
index 24f109112679..1a8c395ddd8b 100644
--- a/src/components/OptionRow.js
+++ b/src/components/OptionRow.js
@@ -3,11 +3,11 @@ import PropTypes from 'prop-types';
import React, {useEffect, useRef, useState} from 'react';
import {InteractionManager, StyleSheet, View} from 'react-native';
import _ from 'underscore';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as ReportUtils from '@libs/ReportUtils';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import Button from './Button';
import DisplayNames from './DisplayNames';
diff --git a/src/components/OptionsList/BaseOptionsList.js b/src/components/OptionsList/BaseOptionsList.js
index 31bc4d4070af..d22df00bd0b3 100644
--- a/src/components/OptionsList/BaseOptionsList.js
+++ b/src/components/OptionsList/BaseOptionsList.js
@@ -7,7 +7,7 @@ import OptionsListSkeletonView from '@components/OptionsListSkeletonView';
import SectionList from '@components/SectionList';
import Text from '@components/Text';
import usePrevious from '@hooks/usePrevious';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import {defaultProps as optionsListDefaultProps, propTypes as optionsListPropTypes} from './optionsListPropTypes';
diff --git a/src/components/PDFView/PDFInfoMessage.js b/src/components/PDFView/PDFInfoMessage.js
index 9ec53b2ae375..7c234b19daa5 100644
--- a/src/components/PDFView/PDFInfoMessage.js
+++ b/src/components/PDFView/PDFInfoMessage.js
@@ -6,7 +6,7 @@ import * as Expensicons from '@components/Icon/Expensicons';
import Text from '@components/Text';
import TextLink from '@components/TextLink';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
const propTypes = {
diff --git a/src/components/PDFView/PDFPasswordForm.js b/src/components/PDFView/PDFPasswordForm.js
index 2c33d8d93a31..42d2ebbb771e 100644
--- a/src/components/PDFView/PDFPasswordForm.js
+++ b/src/components/PDFView/PDFPasswordForm.js
@@ -6,10 +6,10 @@ import Button from '@components/Button';
import Text from '@components/Text';
import TextInput from '@components/TextInput';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as Browser from '@libs/Browser';
import shouldDelayFocus from '@libs/shouldDelayFocus';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import PDFInfoMessage from './PDFInfoMessage';
diff --git a/src/components/ParentNavigationSubtitle.tsx b/src/components/ParentNavigationSubtitle.tsx
index c12cefe6bead..b3f1653fda1a 100644
--- a/src/components/ParentNavigationSubtitle.tsx
+++ b/src/components/ParentNavigationSubtitle.tsx
@@ -1,8 +1,8 @@
import React from 'react';
import {StyleProp, ViewStyle} from 'react-native';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import {ParentNavigationSummaryParams} from '@src/languages/types';
import ROUTES from '@src/ROUTES';
diff --git a/src/components/Picker/BasePicker.tsx b/src/components/Picker/BasePicker.tsx
index dfb2d6332da5..c6b5026b1938 100644
--- a/src/components/Picker/BasePicker.tsx
+++ b/src/components/Picker/BasePicker.tsx
@@ -7,8 +7,10 @@ import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import Text from '@components/Text';
import useScrollContext from '@hooks/useScrollContext';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
+import getOperatingSystem from '@libs/getOperatingSystem';
+import CONST from '@src/CONST';
import type {BasePickerHandle, BasePickerProps} from './types';
type IconToRender = () => ReactElement;
@@ -47,7 +49,7 @@ function BasePicker(
// Windows will reuse the text color of the select for each one of the options
// so we might need to color accordingly so it doesn't blend with the background.
- const pickerPlaceholder = Object.keys(placeholder).length > 0 ? {...placeholder, color: theme.pickerOptionsTextColor} : {};
+ const pickerPlaceholder = Object.keys(placeholder).length > 0 ? {...placeholder, color: theme.text} : {};
useEffect(() => {
if (!!value || !items || items.length !== 1 || !onInputChange) {
@@ -136,6 +138,17 @@ function BasePicker(
},
}));
+ /**
+ * We pass light text on Android, since Android Native alerts have a dark background in all themes for now.
+ */
+ const itemColor = useMemo(() => {
+ if (getOperatingSystem() === CONST.OS.ANDROID) {
+ return theme.textLight;
+ }
+
+ return theme.text;
+ }, [theme]);
+
const hasError = !!errorText;
if (isDisabled) {
@@ -165,7 +178,7 @@ function BasePicker(
({...item, color: theme.pickerOptionsTextColor}))}
+ items={items.map((item) => ({...item, color: itemColor}))}
style={size === 'normal' ? styles.picker(isDisabled, backgroundColor) : styles.pickerSmall(backgroundColor)}
useNativeAndroidPickerStyle={false}
placeholder={pickerPlaceholder}
diff --git a/src/components/PinButton.js b/src/components/PinButton.js
deleted file mode 100644
index 4b336b1d71d4..000000000000
--- a/src/components/PinButton.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import React from 'react';
-import reportPropTypes from '@pages/reportPropTypes';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
-import * as Report from '@userActions/Report';
-import * as Session from '@userActions/Session';
-import CONST from '@src/CONST';
-import Icon from './Icon';
-import * as Expensicons from './Icon/Expensicons';
-import PressableWithFeedback from './Pressable/PressableWithFeedback';
-import Tooltip from './Tooltip';
-import withLocalize, {withLocalizePropTypes} from './withLocalize';
-
-const propTypes = {
- /** Report to pin */
- report: reportPropTypes,
- ...withLocalizePropTypes,
-};
-
-const defaultProps = {
- report: null,
-};
-
-function PinButton(props) {
- const theme = useTheme();
- const styles = useThemeStyles();
- return (
-
- Report.togglePinnedState(props.report.reportID, props.report.isPinned))}
- style={[styles.touchableButtonImage]}
- ariaChecked={props.report.isPinned}
- accessibilityLabel={props.report.isPinned ? props.translate('common.unPin') : props.translate('common.pin')}
- role={CONST.ROLE.BUTTON}
- >
-
-
-
- );
-}
-
-PinButton.displayName = 'PinButton';
-PinButton.propTypes = propTypes;
-PinButton.defaultProps = defaultProps;
-
-export default withLocalize(PinButton);
diff --git a/src/components/PinButton.tsx b/src/components/PinButton.tsx
new file mode 100644
index 000000000000..2ae74853d571
--- /dev/null
+++ b/src/components/PinButton.tsx
@@ -0,0 +1,43 @@
+import React from 'react';
+import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
+import * as ReportActions from '@userActions/Report';
+import * as Session from '@userActions/Session';
+import CONST from '@src/CONST';
+import type {Report} from '@src/types/onyx';
+import Icon from './Icon';
+import * as Expensicons from './Icon/Expensicons';
+import PressableWithFeedback from './Pressable/PressableWithFeedback';
+import Tooltip from './Tooltip';
+
+type PinButtonProps = {
+ /** Report to pin */
+ report: Report;
+};
+
+function PinButton({report}: PinButtonProps) {
+ const theme = useTheme();
+ const styles = useThemeStyles();
+ const {translate} = useLocalize();
+
+ return (
+
+ ReportActions.togglePinnedState(report.reportID, report.isPinned ?? false))}
+ style={styles.touchableButtonImage}
+ accessibilityLabel={report.isPinned ? translate('common.unPin') : translate('common.pin')}
+ role={CONST.ROLE.BUTTON}
+ >
+
+
+
+ );
+}
+
+PinButton.displayName = 'PinButton';
+
+export default PinButton;
diff --git a/src/components/PopoverMenu/index.js b/src/components/PopoverMenu/index.js
index 2106efb9d31e..1bb50dfbd89e 100644
--- a/src/components/PopoverMenu/index.js
+++ b/src/components/PopoverMenu/index.js
@@ -9,8 +9,8 @@ import Text from '@components/Text';
import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions';
import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager';
import useKeyboardShortcut from '@hooks/useKeyboardShortcut';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import {defaultProps as createMenuDefaultProps, propTypes as createMenuPropTypes} from './popoverMenuPropTypes';
diff --git a/src/components/PopoverWithMeasuredContent.js b/src/components/PopoverWithMeasuredContent.js
index b2c94c81770f..7de5be113e53 100644
--- a/src/components/PopoverWithMeasuredContent.js
+++ b/src/components/PopoverWithMeasuredContent.js
@@ -2,9 +2,9 @@ import PropTypes from 'prop-types';
import React, {useMemo, useState} from 'react';
import {View} from 'react-native';
import _ from 'underscore';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
-import PopoverWithMeasuredContentStyleUtils from '@styles/PopoverWithMeasuredContentStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
+import PopoverWithMeasuredContentUtils from '@libs/PopoverWithMeasuredContentUtils';
import CONST from '@src/CONST';
import Popover from './Popover';
import {defaultProps as defaultPopoverProps, propTypes as popoverPropTypes} from './Popover/popoverPropTypes';
@@ -128,8 +128,8 @@ function PopoverWithMeasuredContent(props) {
};
}, [props.anchorPosition, props.anchorAlignment, popoverWidth, popoverHeight]);
- const horizontalShift = PopoverWithMeasuredContentStyleUtils.computeHorizontalShift(adjustedAnchorPosition.left, popoverWidth, windowWidth);
- const verticalShift = PopoverWithMeasuredContentStyleUtils.computeVerticalShift(adjustedAnchorPosition.top, popoverHeight, windowHeight);
+ const horizontalShift = PopoverWithMeasuredContentUtils.computeHorizontalShift(adjustedAnchorPosition.left, popoverWidth, windowWidth);
+ const verticalShift = PopoverWithMeasuredContentUtils.computeVerticalShift(adjustedAnchorPosition.top, popoverHeight, windowHeight);
const shiftedAnchorPosition = {
left: adjustedAnchorPosition.left + horizontalShift,
bottom: windowHeight - (adjustedAnchorPosition.top + popoverHeight) - verticalShift,
diff --git a/src/components/PopoverWithoutOverlay/index.js b/src/components/PopoverWithoutOverlay/index.js
index c13d9e1a0931..43ca1c0de818 100644
--- a/src/components/PopoverWithoutOverlay/index.js
+++ b/src/components/PopoverWithoutOverlay/index.js
@@ -5,8 +5,8 @@ import {defaultProps, propTypes} from '@components/Popover/popoverPropTypes';
import {PopoverContext} from '@components/PopoverProvider';
import withWindowDimensions from '@components/withWindowDimensions';
import useSafeAreaInsets from '@hooks/useSafeAreaInsets';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as Modal from '@userActions/Modal';
function Popover(props) {
diff --git a/src/components/Pressable/GenericPressable/BaseGenericPressable.tsx b/src/components/Pressable/GenericPressable/BaseGenericPressable.tsx
index 0d0ed33d5138..0a9f544bcee1 100644
--- a/src/components/Pressable/GenericPressable/BaseGenericPressable.tsx
+++ b/src/components/Pressable/GenericPressable/BaseGenericPressable.tsx
@@ -2,11 +2,11 @@ import React, {ForwardedRef, forwardRef, useCallback, useEffect, useMemo} from '
// eslint-disable-next-line no-restricted-imports
import {GestureResponderEvent, Pressable, View} from 'react-native';
import useSingleExecution from '@hooks/useSingleExecution';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import Accessibility from '@libs/Accessibility';
import HapticFeedback from '@libs/HapticFeedback';
import KeyboardShortcut from '@libs/KeyboardShortcut';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import PressableProps, {PressableRef} from './types';
diff --git a/src/components/Pressable/PressableWithDelayToggle.tsx b/src/components/Pressable/PressableWithDelayToggle.tsx
index 5e072dc6b1a1..e768f0d0b3fa 100644
--- a/src/components/Pressable/PressableWithDelayToggle.tsx
+++ b/src/components/Pressable/PressableWithDelayToggle.tsx
@@ -6,10 +6,10 @@ import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import Text from '@components/Text';
import Tooltip from '@components/Tooltip';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import useThrottledButtonState from '@hooks/useThrottledButtonState';
import getButtonState from '@libs/getButtonState';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import PressableProps, {PressableRef} from './GenericPressable/types';
import PressableWithoutFeedback from './PressableWithoutFeedback';
@@ -19,7 +19,7 @@ type PressableWithDelayToggleProps = PressableProps & {
text: string;
/** The text to display once the pressable is pressed */
- textChecked: string;
+ textChecked?: string;
/** The tooltip text to display */
tooltipText: string;
diff --git a/src/components/PressableWithSecondaryInteraction/index.tsx b/src/components/PressableWithSecondaryInteraction/index.tsx
index 437123dbe952..0cb53e40325a 100644
--- a/src/components/PressableWithSecondaryInteraction/index.tsx
+++ b/src/components/PressableWithSecondaryInteraction/index.tsx
@@ -2,9 +2,9 @@ import React, {forwardRef, useEffect, useRef} from 'react';
import {GestureResponderEvent} from 'react-native';
import {PressableRef} from '@components/Pressable/GenericPressable/types';
import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import PressableWithSecondaryInteractionProps from './types';
/** This is a special Pressable that calls onSecondaryInteraction when LongPressed, or right-clicked. */
diff --git a/src/components/QRCode.tsx b/src/components/QRCode.tsx
index 5363281ddc67..99a3a1f64118 100644
--- a/src/components/QRCode.tsx
+++ b/src/components/QRCode.tsx
@@ -1,7 +1,7 @@
import React, {Ref} from 'react';
import {ImageSourcePropType} from 'react-native';
import QRCodeLibrary from 'react-native-qrcode-svg';
-import useTheme from '@styles/themes/useTheme';
+import useTheme from '@hooks/useTheme';
import CONST from '@src/CONST';
type LogoRatio = typeof CONST.QR.DEFAULT_LOGO_SIZE_RATIO | typeof CONST.QR.EXPENSIFY_LOGO_SIZE_RATIO;
diff --git a/src/components/QRShare/index.js b/src/components/QRShare/index.js
index f7bb5cb5f788..f644db844e53 100644
--- a/src/components/QRShare/index.js
+++ b/src/components/QRShare/index.js
@@ -4,8 +4,8 @@ import _ from 'underscore';
import ExpensifyWordmark from '@assets/images/expensify-wordmark.svg';
import QRCode from '@components/QRCode';
import Text from '@components/Text';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
import {qrShareDefaultProps, qrSharePropTypes} from './propTypes';
diff --git a/src/components/RNTextInput.tsx b/src/components/RNTextInput.tsx
index 28555abe3266..ff812e7c799b 100644
--- a/src/components/RNTextInput.tsx
+++ b/src/components/RNTextInput.tsx
@@ -2,16 +2,20 @@ import React, {ForwardedRef} from 'react';
// eslint-disable-next-line no-restricted-imports
import {TextInput, TextInputProps} from 'react-native';
import Animated, {AnimatedProps} from 'react-native-reanimated';
+import useTheme from '@hooks/useTheme';
// Convert the underlying TextInput into an Animated component so that we can take an animated ref and pass it to a worklet
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function RNTextInputWithRef(props: TextInputProps, ref: ForwardedRef>>) {
+ const theme = useTheme();
+
return (
{
if (typeof ref !== 'function') {
return;
diff --git a/src/components/RadioButton.tsx b/src/components/RadioButton.tsx
index 68fcda6677c8..2d18ccb480b8 100644
--- a/src/components/RadioButton.tsx
+++ b/src/components/RadioButton.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import {View} from 'react-native';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
import Icon from './Icon';
import * as Expensicons from './Icon/Expensicons';
diff --git a/src/components/RadioButtonWithLabel.tsx b/src/components/RadioButtonWithLabel.tsx
index 7d8df23bae49..4c223262ac50 100644
--- a/src/components/RadioButtonWithLabel.tsx
+++ b/src/components/RadioButtonWithLabel.tsx
@@ -1,6 +1,6 @@
import React, {ComponentType} from 'react';
import {StyleProp, View, ViewStyle} from 'react-native';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import FormHelpMessage from './FormHelpMessage';
import * as Pressables from './Pressable';
import RadioButton from './RadioButton';
diff --git a/src/components/RadioButtons.tsx b/src/components/RadioButtons.tsx
index b83710bd85bf..8aa3ef7e8ffe 100644
--- a/src/components/RadioButtons.tsx
+++ b/src/components/RadioButtons.tsx
@@ -1,5 +1,5 @@
import React, {useState} from 'react';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import RadioButtonWithLabel from './RadioButtonWithLabel';
type Choice = {
diff --git a/src/components/Reactions/AddReactionBubble.js b/src/components/Reactions/AddReactionBubble.js
index 128eafd51ee8..61ad4ce76d64 100644
--- a/src/components/Reactions/AddReactionBubble.js
+++ b/src/components/Reactions/AddReactionBubble.js
@@ -7,9 +7,9 @@ import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
import Text from '@components/Text';
import Tooltip from '@components/Tooltip/PopoverAnchorTooltip';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import getButtonState from '@libs/getButtonState';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import * as EmojiPickerAction from '@userActions/EmojiPickerAction';
import * as Session from '@userActions/Session';
diff --git a/src/components/Reactions/EmojiReactionBubble.js b/src/components/Reactions/EmojiReactionBubble.js
index 7ec72468ee91..3fd22a758f67 100644
--- a/src/components/Reactions/EmojiReactionBubble.js
+++ b/src/components/Reactions/EmojiReactionBubble.js
@@ -4,8 +4,8 @@ import PressableWithSecondaryInteraction from '@components/PressableWithSecondar
import Text from '@components/Text';
import {withCurrentUserPersonalDetailsDefaultProps} from '@components/withCurrentUserPersonalDetails';
import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
const propTypes = {
diff --git a/src/components/Reactions/MiniQuickEmojiReactions.js b/src/components/Reactions/MiniQuickEmojiReactions.js
index 92913a7c4c5e..34d336887031 100644
--- a/src/components/Reactions/MiniQuickEmojiReactions.js
+++ b/src/components/Reactions/MiniQuickEmojiReactions.js
@@ -8,11 +8,11 @@ import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import Text from '@components/Text';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import * as EmojiUtils from '@libs/EmojiUtils';
import getButtonState from '@libs/getButtonState';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import * as EmojiPickerAction from '@userActions/EmojiPickerAction';
import * as Session from '@userActions/Session';
import CONST from '@src/CONST';
diff --git a/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js b/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js
index 9d32b0240a23..c932632f7bff 100644
--- a/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js
+++ b/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js
@@ -7,8 +7,8 @@ import AddReactionBubble from '@components/Reactions/AddReactionBubble';
import EmojiReactionBubble from '@components/Reactions/EmojiReactionBubble';
import EmojiReactionsPropTypes from '@components/Reactions/EmojiReactionsPropTypes';
import Tooltip from '@components/Tooltip';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as EmojiUtils from '@libs/EmojiUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Session from '@userActions/Session';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/components/Reactions/ReactionTooltipContent.js b/src/components/Reactions/ReactionTooltipContent.js
index 1a7a06e9487d..bb6b03c5918b 100644
--- a/src/components/Reactions/ReactionTooltipContent.js
+++ b/src/components/Reactions/ReactionTooltipContent.js
@@ -5,8 +5,8 @@ import _ from 'underscore';
import Text from '@components/Text';
import {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails';
import withLocalize from '@components/withLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
-import useThemeStyles from '@styles/useThemeStyles';
const propTypes = {
/**
diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js
index 7c504e35cb9f..547f4089857f 100644
--- a/src/components/Reactions/ReportActionItemEmojiReactions.js
+++ b/src/components/Reactions/ReportActionItemEmojiReactions.js
@@ -7,11 +7,11 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback';
import Tooltip from '@components/Tooltip';
import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails';
import withLocalize from '@components/withLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import * as EmojiUtils from '@libs/EmojiUtils';
import reportActionPropTypes from '@pages/home/report/reportActionPropTypes';
import {ReactionListContext} from '@pages/home/ReportScreenContext';
-import useThemeStyles from '@styles/useThemeStyles';
import AddReactionBubble from './AddReactionBubble';
import EmojiReactionBubble from './EmojiReactionBubble';
import EmojiReactionsPropTypes from './EmojiReactionsPropTypes';
diff --git a/src/components/ReceiptEmptyState.js b/src/components/ReceiptEmptyState.js
index 6af1cadd48c5..371518dd9a77 100644
--- a/src/components/ReceiptEmptyState.js
+++ b/src/components/ReceiptEmptyState.js
@@ -1,6 +1,6 @@
import PropTypes from 'prop-types';
import React from 'react';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
import Icon from './Icon';
import * as Expensicons from './Icon/Expensicons';
diff --git a/src/components/ReimbursementAccountLoadingIndicator.js b/src/components/ReimbursementAccountLoadingIndicator.js
index 72bf3ce1896d..bc0e70e64419 100644
--- a/src/components/ReimbursementAccountLoadingIndicator.js
+++ b/src/components/ReimbursementAccountLoadingIndicator.js
@@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
import React from 'react';
import {StyleSheet, View} from 'react-native';
import useLocalize from '@hooks/useLocalize';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import FullPageOfflineBlockingView from './BlockingViews/FullPageOfflineBlockingView';
import FullScreenLoadingIndicator from './FullscreenLoadingIndicator';
import HeaderWithBackButton from './HeaderWithBackButton';
diff --git a/src/components/ReportActionItem/ChronosOOOListActions.js b/src/components/ReportActionItem/ChronosOOOListActions.js
index 7c918b6a2d9c..f90ae67796b9 100644
--- a/src/components/ReportActionItem/ChronosOOOListActions.js
+++ b/src/components/ReportActionItem/ChronosOOOListActions.js
@@ -7,9 +7,9 @@ import Button from '@components/Button';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import Text from '@components/Text';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import DateUtils from '@libs/DateUtils';
import reportActionPropTypes from '@pages/home/report/reportActionPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Chronos from '@userActions/Chronos';
const propTypes = {
diff --git a/src/components/ReportActionItem/MoneyReportView.js b/src/components/ReportActionItem/MoneyReportView.js
index c52d4abe371c..c12659e94673 100644
--- a/src/components/ReportActionItem/MoneyReportView.js
+++ b/src/components/ReportActionItem/MoneyReportView.js
@@ -7,13 +7,13 @@ import SpacerView from '@components/SpacerView';
import Text from '@components/Text';
import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions';
import useLocalize from '@hooks/useLocalize';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as CurrencyUtils from '@libs/CurrencyUtils';
import * as ReportUtils from '@libs/ReportUtils';
import AnimatedEmptyStateBackground from '@pages/home/report/AnimatedEmptyStateBackground';
import reportPropTypes from '@pages/reportPropTypes';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
const propTypes = {
diff --git a/src/components/ReportActionItem/MoneyRequestAction.js b/src/components/ReportActionItem/MoneyRequestAction.js
index 040682990d2c..e0a3152a41b4 100644
--- a/src/components/ReportActionItem/MoneyRequestAction.js
+++ b/src/components/ReportActionItem/MoneyRequestAction.js
@@ -8,6 +8,7 @@ import {withNetwork} from '@components/OnyxProvider';
import refPropTypes from '@components/refPropTypes';
import RenderHTML from '@components/RenderHTML';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import * as IOUUtils from '@libs/IOUUtils';
import Navigation from '@libs/Navigation/Navigation';
@@ -17,7 +18,6 @@ import * as ReportUtils from '@libs/ReportUtils';
import reportActionPropTypes from '@pages/home/report/reportActionPropTypes';
import iouReportPropTypes from '@pages/iouReportPropTypes';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Report from '@userActions/Report';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/components/ReportActionItem/MoneyRequestPreview.js b/src/components/ReportActionItem/MoneyRequestPreview.js
index 210a0575a310..12c6d0629370 100644
--- a/src/components/ReportActionItem/MoneyRequestPreview.js
+++ b/src/components/ReportActionItem/MoneyRequestPreview.js
@@ -14,9 +14,11 @@ import refPropTypes from '@components/refPropTypes';
import {showContextMenuForReport} from '@components/ShowContextMenuContext';
import Text from '@components/Text';
import transactionPropTypes from '@components/transactionPropTypes';
-import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useLocalize from '@hooks/useLocalize';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
-import compose from '@libs/compose';
import ControlSelection from '@libs/ControlSelection';
import * as CurrencyUtils from '@libs/CurrencyUtils';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
@@ -28,9 +30,8 @@ import * as ReportUtils from '@libs/ReportUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
import walletTermsPropTypes from '@pages/EnablePayments/walletTermsPropTypes';
import reportActionPropTypes from '@pages/home/report/reportActionPropTypes';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
+import iouReportPropTypes from '@pages/iouReportPropTypes';
+import reportPropTypes from '@pages/reportPropTypes';
import * as PaymentMethods from '@userActions/PaymentMethods';
import * as Report from '@userActions/Report';
import CONST from '@src/CONST';
@@ -64,20 +65,11 @@ const propTypes = {
/* Onyx Props */
- /** Active IOU Report for current report */
- iouReport: PropTypes.shape({
- /** Account ID of the manager in this iou report */
- managerID: PropTypes.number,
+ /** chatReport associated with iouReport */
+ chatReport: reportPropTypes,
- /** Account ID of the creator of this iou report */
- ownerAccountID: PropTypes.number,
-
- /** Outstanding amount in cents of this transaction */
- total: PropTypes.number,
-
- /** Currency of outstanding amount of this transaction */
- currency: PropTypes.string,
- }),
+ /** IOU report data object */
+ iouReport: iouReportPropTypes,
/** True if this is this IOU is a split instead of a 1:1 request */
isBillSplit: PropTypes.bool.isRequired,
@@ -112,8 +104,6 @@ const propTypes = {
/** Whether a message is a whisper */
isWhisper: PropTypes.bool,
-
- ...withLocalizePropTypes,
};
const defaultProps = {
@@ -124,6 +114,7 @@ const defaultProps = {
checkIfContextMenuActive: () => {},
containerStyles: [],
walletTerms: {},
+ chatReport: {},
isHovered: false,
personalDetails: {},
session: {
@@ -138,6 +129,7 @@ function MoneyRequestPreview(props) {
const theme = useTheme();
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
+ const {translate} = useLocalize();
const {isSmallScreenWidth, windowWidth} = useWindowDimensions();
if (_.isEmpty(props.iouReport) && !props.isBillSplit) {
@@ -179,14 +171,9 @@ function MoneyRequestPreview(props) {
const getSettledMessage = () => {
if (isExpensifyCardTransaction) {
- return props.translate('common.done');
- }
- switch (lodashGet(props.action, 'originalMessage.paymentType', '')) {
- case CONST.IOU.PAYMENT_TYPE.EXPENSIFY:
- return props.translate('iou.settledExpensify');
- default:
- return props.translate('iou.settledElsewhere');
+ return translate('common.done');
}
+ return translate('iou.settledExpensify');
};
const showContextMenu = (event) => {
@@ -195,43 +182,43 @@ function MoneyRequestPreview(props) {
const getPreviewHeaderText = () => {
if (isDistanceRequest) {
- return props.translate('common.distance');
+ return translate('common.distance');
}
if (isScanning) {
- return props.translate('common.receipt');
+ return translate('common.receipt');
}
if (props.isBillSplit) {
- return props.translate('iou.split');
+ return translate('iou.split');
}
if (isExpensifyCardTransaction) {
- let message = props.translate('iou.card');
+ let message = translate('iou.card');
if (TransactionUtils.isPending(props.transaction)) {
- message += ` • ${props.translate('iou.pending')}`;
+ message += ` • ${translate('iou.pending')}`;
}
return message;
}
- let message = props.translate('iou.cash');
+ let message = translate('iou.cash');
if (ReportUtils.isGroupPolicyExpenseReport(props.iouReport) && ReportUtils.isReportApproved(props.iouReport) && !ReportUtils.isSettled(props.iouReport)) {
- message += ` • ${props.translate('iou.approved')}`;
+ message += ` • ${translate('iou.approved')}`;
} else if (props.iouReport.isWaitingOnBankAccount) {
- message += ` • ${props.translate('iou.pending')}`;
+ message += ` • ${translate('iou.pending')}`;
} else if (props.iouReport.isCancelledIOU) {
- message += ` • ${props.translate('iou.canceled')}`;
+ message += ` • ${translate('iou.canceled')}`;
}
return message;
};
const getDisplayAmountText = () => {
if (isDistanceRequest) {
- return requestAmount && !hasPendingWaypoints ? CurrencyUtils.convertToDisplayString(requestAmount, props.transaction.currency) : props.translate('common.tbd');
+ return requestAmount && !hasPendingWaypoints ? CurrencyUtils.convertToDisplayString(requestAmount, props.transaction.currency) : translate('common.tbd');
}
if (isScanning) {
- return props.translate('iou.receiptScanning');
+ return translate('iou.receiptScanning');
}
if (TransactionUtils.hasMissingSmartscanFields(props.transaction)) {
@@ -331,14 +318,14 @@ function MoneyRequestPreview(props) {
{shouldShowMerchant && !props.isBillSplit && (
- {hasPendingWaypoints ? requestMerchant.replace(CONST.REGEX.FIRST_SPACE, props.translate('common.tbd')) : requestMerchant}
+ {hasPendingWaypoints ? requestMerchant.replace(CONST.REGEX.FIRST_SPACE, translate('common.tbd')) : requestMerchant}
)}
{!isCurrentUserManager && props.shouldShowPendingConversionMessage && (
- {props.translate('iou.pendingConversionMessage')}
+ {translate('iou.pendingConversionMessage')}
)}
{(shouldShowDescription || (shouldShowMerchant && props.isBillSplit)) && (
{shouldShowDescription ? description : requestMerchant}
@@ -346,7 +333,7 @@ function MoneyRequestPreview(props) {
{props.isBillSplit && !_.isEmpty(participantAccountIDs) && requestAmount > 0 && (
- {props.translate('iou.amountEach', {
+ {translate('iou.amountEach', {
amount: CurrencyUtils.convertToDisplayString(
IOUUtils.calculateAmount(isPolicyExpenseChat ? 1 : participantAccountIDs.length - 1, requestAmount, requestCurrency),
requestCurrency,
@@ -374,7 +361,7 @@ function MoneyRequestPreview(props) {
onPressIn={() => DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()}
onPressOut={() => ControlSelection.unblock()}
onLongPress={showContextMenu}
- accessibilityLabel={props.isBillSplit ? props.translate('iou.split') : props.translate('iou.cash')}
+ accessibilityLabel={props.isBillSplit ? translate('iou.split') : translate('iou.cash')}
accessibilityHint={CurrencyUtils.convertToDisplayString(requestAmount, requestCurrency)}
style={[styles.moneyRequestPreviewBox, ...props.containerStyles, shouldDisableOnPress && styles.cursorDefault]}
>
@@ -387,26 +374,23 @@ MoneyRequestPreview.propTypes = propTypes;
MoneyRequestPreview.defaultProps = defaultProps;
MoneyRequestPreview.displayName = 'MoneyRequestPreview';
-export default compose(
- withLocalize,
- withOnyx({
- personalDetails: {
- key: ONYXKEYS.PERSONAL_DETAILS_LIST,
- },
- chatReport: {
- key: ({chatReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`,
- },
- iouReport: {
- key: ({iouReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`,
- },
- session: {
- key: ONYXKEYS.SESSION,
- },
- transaction: {
- key: ({action}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${(action && action.originalMessage && action.originalMessage.IOUTransactionID) || 0}`,
- },
- walletTerms: {
- key: ONYXKEYS.WALLET_TERMS,
- },
- }),
-)(MoneyRequestPreview);
+export default withOnyx({
+ personalDetails: {
+ key: ONYXKEYS.PERSONAL_DETAILS_LIST,
+ },
+ chatReport: {
+ key: ({chatReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`,
+ },
+ iouReport: {
+ key: ({iouReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`,
+ },
+ session: {
+ key: ONYXKEYS.SESSION,
+ },
+ transaction: {
+ key: ({action}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${(action && action.originalMessage && action.originalMessage.IOUTransactionID) || 0}`,
+ },
+ walletTerms: {
+ key: ONYXKEYS.WALLET_TERMS,
+ },
+})(MoneyRequestPreview);
diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js
index 1afc909b54df..817c88d456db 100644
--- a/src/components/ReportActionItem/MoneyRequestView.js
+++ b/src/components/ReportActionItem/MoneyRequestView.js
@@ -17,6 +17,9 @@ import transactionPropTypes from '@components/transactionPropTypes';
import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails';
import useLocalize from '@hooks/useLocalize';
import usePermissions from '@hooks/usePermissions';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as CardUtils from '@libs/CardUtils';
import compose from '@libs/compose';
@@ -32,9 +35,6 @@ import AnimatedEmptyStateBackground from '@pages/home/report/AnimatedEmptyStateB
import reportActionPropTypes from '@pages/home/report/reportActionPropTypes';
import iouReportPropTypes from '@pages/iouReportPropTypes';
import reportPropTypes from '@pages/reportPropTypes';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/components/ReportActionItem/RenameAction.js b/src/components/ReportActionItem/RenameAction.js
index b4f4b23c9ebf..52039b7b593b 100644
--- a/src/components/ReportActionItem/RenameAction.js
+++ b/src/components/ReportActionItem/RenameAction.js
@@ -4,9 +4,9 @@ import React from 'react';
import Text from '@components/Text';
import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import reportActionPropTypes from '@pages/home/report/reportActionPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
const propTypes = {
/** All the data of the action */
diff --git a/src/components/ReportActionItem/ReportActionItemImage.js b/src/components/ReportActionItem/ReportActionItemImage.js
index 534a92dae830..2c5ef22b1b8e 100644
--- a/src/components/ReportActionItem/ReportActionItemImage.js
+++ b/src/components/ReportActionItem/ReportActionItemImage.js
@@ -10,9 +10,9 @@ import {ShowContextMenuContext} from '@components/ShowContextMenuContext';
import ThumbnailImage from '@components/ThumbnailImage';
import transactionPropTypes from '@components/transactionPropTypes';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as TransactionUtils from '@libs/TransactionUtils';
import tryResolveUrlFromApiRoot from '@libs/tryResolveUrlFromApiRoot';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
const propTypes = {
diff --git a/src/components/ReportActionItem/ReportActionItemImages.js b/src/components/ReportActionItem/ReportActionItemImages.js
index b20513d63fe3..96f919aea750 100644
--- a/src/components/ReportActionItem/ReportActionItemImages.js
+++ b/src/components/ReportActionItem/ReportActionItemImages.js
@@ -5,9 +5,9 @@ import {Polygon, Svg} from 'react-native-svg';
import _ from 'underscore';
import Text from '@components/Text';
import transactionPropTypes from '@components/transactionPropTypes';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
import ReportActionItemImage from './ReportActionItemImage';
diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js
index e6b387f362ef..aa3c02b76fc0 100644
--- a/src/components/ReportActionItem/ReportPreview.js
+++ b/src/components/ReportActionItem/ReportPreview.js
@@ -14,6 +14,8 @@ import {showContextMenuForReport} from '@components/ShowContextMenuContext';
import Text from '@components/Text';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import ControlSelection from '@libs/ControlSelection';
import * as CurrencyUtils from '@libs/CurrencyUtils';
@@ -25,8 +27,6 @@ import * as ReportUtils from '@libs/ReportUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
import reportActionPropTypes from '@pages/home/report/reportActionPropTypes';
import reportPropTypes from '@pages/reportPropTypes';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/components/ReportActionItem/TaskAction.js b/src/components/ReportActionItem/TaskAction.js
deleted file mode 100644
index d79bb6f78b93..000000000000
--- a/src/components/ReportActionItem/TaskAction.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import PropTypes from 'prop-types';
-import React from 'react';
-import {View} from 'react-native';
-import Text from '@components/Text';
-import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
-import * as TaskUtils from '@libs/TaskUtils';
-import useThemeStyles from '@styles/useThemeStyles';
-
-const propTypes = {
- /** Name of the reportAction action */
- actionName: PropTypes.string.isRequired,
-
- /** The ID of the associated taskReport */
- // eslint-disable-next-line react/no-unused-prop-types -- This is used in the withOnyx HOC
- taskReportID: PropTypes.string.isRequired,
-
- ...withLocalizePropTypes,
-};
-
-function TaskAction(props) {
- const styles = useThemeStyles();
- return (
- <>
-
- {TaskUtils.getTaskReportActionMessage(props.actionName)}
-
- >
- );
-}
-
-TaskAction.propTypes = propTypes;
-TaskAction.displayName = 'TaskAction';
-
-export default withLocalize(TaskAction);
diff --git a/src/components/ReportActionItem/TaskAction.tsx b/src/components/ReportActionItem/TaskAction.tsx
new file mode 100644
index 000000000000..b10be4e86fe8
--- /dev/null
+++ b/src/components/ReportActionItem/TaskAction.tsx
@@ -0,0 +1,24 @@
+import React from 'react';
+import {View} from 'react-native';
+import Text from '@components/Text';
+import useThemeStyles from '@hooks/useThemeStyles';
+import * as TaskUtils from '@libs/TaskUtils';
+
+type TaskActionProps = {
+ /** Name of the reportAction action */
+ actionName: string;
+};
+
+function TaskAction({actionName}: TaskActionProps) {
+ const styles = useThemeStyles();
+
+ return (
+
+ {TaskUtils.getTaskReportActionMessage(actionName)}
+
+ );
+}
+
+TaskAction.displayName = 'TaskAction';
+
+export default TaskAction;
diff --git a/src/components/ReportActionItem/TaskPreview.js b/src/components/ReportActionItem/TaskPreview.js
index 3b04364ee69a..a7728045f407 100644
--- a/src/components/ReportActionItem/TaskPreview.js
+++ b/src/components/ReportActionItem/TaskPreview.js
@@ -14,6 +14,8 @@ import RenderHTML from '@components/RenderHTML';
import {showContextMenuForReport} from '@components/ShowContextMenuContext';
import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import ControlSelection from '@libs/ControlSelection';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
@@ -23,8 +25,6 @@ import Navigation from '@libs/Navigation/Navigation';
import * as ReportUtils from '@libs/ReportUtils';
import * as TaskUtils from '@libs/TaskUtils';
import reportActionPropTypes from '@pages/home/report/reportActionPropTypes';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Session from '@userActions/Session';
import * as Task from '@userActions/Task';
import CONST from '@src/CONST';
@@ -54,6 +54,12 @@ const propTypes = {
ownerAccountID: PropTypes.number,
}),
+ /** The policy of root parent report */
+ rootParentReportpolicy: PropTypes.shape({
+ /** The role of current user */
+ role: PropTypes.string,
+ }),
+
/** The chat report associated with taskReport */
chatReportID: PropTypes.string.isRequired,
@@ -72,6 +78,7 @@ const propTypes = {
const defaultProps = {
...withCurrentUserPersonalDetailsDefaultProps,
taskReport: {},
+ rootParentReportpolicy: {},
isHovered: false,
};
@@ -116,7 +123,7 @@ function TaskPreview(props) {
style={[styles.mr2]}
containerStyle={[styles.taskCheckbox]}
isChecked={isTaskCompleted}
- disabled={!Task.canModifyTask(props.taskReport, props.currentUserPersonalDetails.accountID)}
+ disabled={!Task.canModifyTask(props.taskReport, props.currentUserPersonalDetails.accountID, lodashGet(props.rootParentReportpolicy, 'role', ''))}
onPress={Session.checkIfActionIsAllowed(() => {
if (isTaskCompleted) {
Task.reopenTask(props.taskReport);
@@ -149,5 +156,9 @@ export default compose(
key: ({taskReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${taskReportID}`,
initialValue: {},
},
+ rootParentReportpolicy: {
+ key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID || '0'}`,
+ selector: (policy) => _.pick(policy, ['role']),
+ },
}),
)(TaskPreview);
diff --git a/src/components/ReportActionItem/TaskView.js b/src/components/ReportActionItem/TaskView.js
index ea02dba705a6..7f7b177136ed 100644
--- a/src/components/ReportActionItem/TaskView.js
+++ b/src/components/ReportActionItem/TaskView.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import React, {useEffect} from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
+import _ from 'underscore';
import Checkbox from '@components/Checkbox';
import Hoverable from '@components/Hoverable';
import Icon from '@components/Icon';
@@ -17,6 +18,8 @@ import Text from '@components/Text';
import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import withWindowDimensions from '@components/withWindowDimensions';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import convertToLTR from '@libs/convertToLTR';
import getButtonState from '@libs/getButtonState';
@@ -24,8 +27,6 @@ import Navigation from '@libs/Navigation/Navigation';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as ReportUtils from '@libs/ReportUtils';
import reportPropTypes from '@pages/reportPropTypes';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Session from '@userActions/Session';
import * as Task from '@userActions/Task';
import CONST from '@src/CONST';
@@ -36,6 +37,12 @@ const propTypes = {
/** The report currently being looked at */
report: reportPropTypes.isRequired,
+ /** The policy of root parent report */
+ policy: PropTypes.shape({
+ /** The role of current user */
+ role: PropTypes.string,
+ }),
+
/** Whether we should display the horizontal rule below the component */
shouldShowHorizontalRule: PropTypes.bool.isRequired,
@@ -44,6 +51,10 @@ const propTypes = {
...withCurrentUserPersonalDetailsPropTypes,
};
+const defaultProps = {
+ policy: {},
+};
+
function TaskView(props) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
@@ -55,7 +66,7 @@ function TaskView(props) {
const assigneeTooltipDetails = ReportUtils.getDisplayNamesWithTooltips(OptionsListUtils.getPersonalDetailsForAccountIDs([props.report.managerID], props.personalDetails), false);
const isCompleted = ReportUtils.isCompletedTaskReport(props.report);
const isOpen = ReportUtils.isOpenTaskReport(props.report);
- const canModifyTask = Task.canModifyTask(props.report, props.currentUserPersonalDetails.accountID);
+ const canModifyTask = Task.canModifyTask(props.report, props.currentUserPersonalDetails.accountID, lodashGet(props.policy, 'role', ''));
const disableState = !canModifyTask;
const isDisableInteractive = !canModifyTask || !isOpen;
const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT;
@@ -188,6 +199,7 @@ function TaskView(props) {
}
TaskView.propTypes = propTypes;
+TaskView.defaultProps = defaultProps;
TaskView.displayName = 'TaskView';
export default compose(
@@ -198,5 +210,12 @@ export default compose(
personalDetails: {
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
},
+ policy: {
+ key: ({report}) => {
+ const rootParentReport = ReportUtils.getRootParentReport(report);
+ return `${ONYXKEYS.COLLECTION.POLICY}${rootParentReport ? rootParentReport.policyID : '0'}`;
+ },
+ selector: (policy) => _.pick(policy, ['role']),
+ },
}),
)(TaskView);
diff --git a/src/components/ReportActionsSkeletonView/SkeletonViewLines.tsx b/src/components/ReportActionsSkeletonView/SkeletonViewLines.tsx
index 3451e13298b3..c4c2a3f43eb3 100644
--- a/src/components/ReportActionsSkeletonView/SkeletonViewLines.tsx
+++ b/src/components/ReportActionsSkeletonView/SkeletonViewLines.tsx
@@ -1,8 +1,8 @@
import React from 'react';
import {Circle, Rect} from 'react-native-svg';
import SkeletonViewContentLoader from '@components/SkeletonViewContentLoader';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
type SkeletonViewLinesProps = {
diff --git a/src/components/ReportHeaderSkeletonView.tsx b/src/components/ReportHeaderSkeletonView.tsx
index fc739b5d06d8..91adb26faff5 100644
--- a/src/components/ReportHeaderSkeletonView.tsx
+++ b/src/components/ReportHeaderSkeletonView.tsx
@@ -2,9 +2,9 @@ import React from 'react';
import {View} from 'react-native';
import {Circle, Rect} from 'react-native-svg';
import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import Icon from './Icon';
diff --git a/src/components/ReportWelcomeText.js b/src/components/ReportWelcomeText.js
index 6613517b2ed2..a204d0c59aaf 100644
--- a/src/components/ReportWelcomeText.js
+++ b/src/components/ReportWelcomeText.js
@@ -4,13 +4,13 @@ import React from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReportUtils from '@libs/ReportUtils';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
diff --git a/src/components/RoomHeaderAvatars.js b/src/components/RoomHeaderAvatars.js
index a51219122e0a..5a6363d4a642 100644
--- a/src/components/RoomHeaderAvatars.js
+++ b/src/components/RoomHeaderAvatars.js
@@ -2,10 +2,10 @@ import PropTypes from 'prop-types';
import React, {memo} from 'react';
import {View} from 'react-native';
import _ from 'underscore';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as UserUtils from '@libs/UserUtils';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import AttachmentModal from './AttachmentModal';
import Avatar from './Avatar';
diff --git a/src/components/RoomNameInput/roomNameInputPropTypes.js b/src/components/RoomNameInput/roomNameInputPropTypes.js
index 7f8292f0123e..60be8430b056 100644
--- a/src/components/RoomNameInput/roomNameInputPropTypes.js
+++ b/src/components/RoomNameInput/roomNameInputPropTypes.js
@@ -1,4 +1,5 @@
import PropTypes from 'prop-types';
+import refPropTypes from '@components/refPropTypes';
const propTypes = {
/** Callback to execute when the text input is modified correctly */
@@ -14,7 +15,7 @@ const propTypes = {
errorText: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object]))]),
/** A ref forwarded to the TextInput */
- forwardedRef: PropTypes.func,
+ forwardedRef: refPropTypes,
/** The ID used to uniquely identify the input in a Form */
inputID: PropTypes.string,
diff --git a/src/components/SAMLLoadingIndicator.js b/src/components/SAMLLoadingIndicator.js
index d6d061122eff..84f9098e564f 100644
--- a/src/components/SAMLLoadingIndicator.js
+++ b/src/components/SAMLLoadingIndicator.js
@@ -1,16 +1,17 @@
import React from 'react';
import {StyleSheet, View} from 'react-native';
import useLocalize from '@hooks/useLocalize';
-import themeColors from '@styles/themes/default';
-import useThemeStyles from '@styles/useThemeStyles';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import Icon from './Icon';
import * as Expensicons from './Icon/Expensicons';
import * as Illustrations from './Icon/Illustrations';
import Text from './Text';
function SAMLLoadingIndicator() {
- const {translate} = useLocalize();
+ const theme = useTheme();
const styles = useThemeStyles();
+ const {translate} = useLocalize();
return (
@@ -30,7 +31,7 @@ function SAMLLoadingIndicator() {
diff --git a/src/components/SafeArea/index.ios.tsx b/src/components/SafeArea/index.ios.tsx
index 194919b3eef4..1b2e1881ade3 100644
--- a/src/components/SafeArea/index.ios.tsx
+++ b/src/components/SafeArea/index.ios.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import {SafeAreaView} from 'react-native-safe-area-context';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import SafeAreaProps from './types';
function SafeArea({children}: SafeAreaProps) {
diff --git a/src/components/SafeAreaConsumer/index.android.tsx b/src/components/SafeAreaConsumer/index.android.tsx
index 5117000627cf..4e6c9bbd3528 100644
--- a/src/components/SafeAreaConsumer/index.android.tsx
+++ b/src/components/SafeAreaConsumer/index.android.tsx
@@ -1,8 +1,8 @@
import React from 'react';
// eslint-disable-next-line no-restricted-imports
import {SafeAreaInsetsContext} from 'react-native-safe-area-context';
+import useStyleUtils from '@hooks/useStyleUtils';
import StatusBar from '@libs/StatusBar';
-import useStyleUtils from '@styles/useStyleUtils';
import SafeAreaConsumerProps from './types';
/**
diff --git a/src/components/SafeAreaConsumer/index.tsx b/src/components/SafeAreaConsumer/index.tsx
index 54c5d984be5f..c05e836a2348 100644
--- a/src/components/SafeAreaConsumer/index.tsx
+++ b/src/components/SafeAreaConsumer/index.tsx
@@ -1,7 +1,7 @@
import React from 'react';
// eslint-disable-next-line no-restricted-imports
import {SafeAreaInsetsContext} from 'react-native-safe-area-context';
-import useStyleUtils from '@styles/useStyleUtils';
+import useStyleUtils from '@hooks/useStyleUtils';
import SafeAreaConsumerProps from './types';
/**
diff --git a/src/components/ScreenWrapper/index.js b/src/components/ScreenWrapper/index.js
index 6af67c51ffaf..bd277ffa1ab8 100644
--- a/src/components/ScreenWrapper/index.js
+++ b/src/components/ScreenWrapper/index.js
@@ -14,9 +14,9 @@ import useEnvironment from '@hooks/useEnvironment';
import useInitialDimensions from '@hooks/useInitialWindowDimensions';
import useKeyboardState from '@hooks/useKeyboardState';
import useNetwork from '@hooks/useNetwork';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as Browser from '@libs/Browser';
-import useThemeStyles from '@styles/useThemeStyles';
import toggleTestToolsModal from '@userActions/TestTool';
import CONST from '@src/CONST';
import {defaultProps, propTypes} from './propTypes';
diff --git a/src/components/Search.tsx b/src/components/Search.tsx
new file mode 100644
index 000000000000..10820f44738d
--- /dev/null
+++ b/src/components/Search.tsx
@@ -0,0 +1,62 @@
+import React from 'react';
+import {GestureResponderEvent, StyleProp, View, ViewStyle} from 'react-native';
+import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
+import variables from '@styles/variables';
+import CONST from '@src/CONST';
+import Icon from './Icon';
+import * as Expensicons from './Icon/Expensicons';
+import {PressableWithFeedback} from './Pressable';
+import Text from './Text';
+import Tooltip from './Tooltip';
+
+type SearchProps = {
+ // Callback fired when component is pressed
+ onPress: (event?: GestureResponderEvent | KeyboardEvent) => void;
+
+ // Text explaining what the user can search for
+ placeholder?: string;
+
+ // Text showing up in a tooltip when component is hovered
+ tooltip?: string;
+
+ // Styles to apply on the outer element
+ style?: StyleProp;
+};
+
+function Search({onPress, placeholder, tooltip, style}: SearchProps) {
+ const styles = useThemeStyles();
+ const {translate} = useLocalize();
+
+ return (
+
+
+ {({hovered}) => (
+
+
+
+ {placeholder ?? translate('common.searchWithThreeDots')}
+
+
+ )}
+
+
+ );
+}
+
+Search.displayName = 'Search';
+
+export type {SearchProps};
+export default Search;
diff --git a/src/components/Section.js b/src/components/Section.js
index ad52bf0e3509..a59476fb0c05 100644
--- a/src/components/Section.js
+++ b/src/components/Section.js
@@ -1,7 +1,7 @@
import PropTypes from 'prop-types';
import React from 'react';
import {View} from 'react-native';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import Icon from './Icon';
import MenuItemList from './MenuItemList';
import menuItemPropTypes from './menuItemPropTypes';
diff --git a/src/components/SelectCircle.tsx b/src/components/SelectCircle.tsx
index 7a7e23388722..637c06f4d35f 100644
--- a/src/components/SelectCircle.tsx
+++ b/src/components/SelectCircle.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import {StyleProp, View, ViewStyle} from 'react-native';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import Icon from './Icon';
import * as Expensicons from './Icon/Expensicons';
diff --git a/src/components/SelectionList/BaseListItem.js b/src/components/SelectionList/BaseListItem.js
index 39bd10ca5a9d..ac679f32d103 100644
--- a/src/components/SelectionList/BaseListItem.js
+++ b/src/components/SelectionList/BaseListItem.js
@@ -7,9 +7,9 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback';
import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
import RadioListItem from './RadioListItem';
import {baseListItemPropTypes} from './selectionListPropTypes';
diff --git a/src/components/SelectionList/BaseSelectionList.js b/src/components/SelectionList/BaseSelectionList.js
index ce5d1945fd2a..454127612e0d 100644
--- a/src/components/SelectionList/BaseSelectionList.js
+++ b/src/components/SelectionList/BaseSelectionList.js
@@ -17,9 +17,10 @@ import withKeyboardState, {keyboardStatePropTypes} from '@components/withKeyboar
import useActiveElement from '@hooks/useActiveElement';
import useKeyboardShortcut from '@hooks/useKeyboardShortcut';
import useLocalize from '@hooks/useLocalize';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import Log from '@libs/Log';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import BaseListItem from './BaseListItem';
@@ -55,6 +56,8 @@ function BaseSelectionList({
showConfirmButton = false,
shouldPreventDefaultFocusOnSelectRow = false,
isKeyboardShown = false,
+ containerStyle = [],
+ disableInitialFocusOptionStyle = false,
inputRef = null,
disableKeyboardShortcuts = false,
children,
@@ -63,6 +66,7 @@ function BaseSelectionList({
}) {
const theme = useTheme();
const styles = useThemeStyles();
+ const StyleUtils = useStyleUtils();
const {translate} = useLocalize();
const listRef = useRef(null);
const textInputRef = useRef(null);
@@ -310,6 +314,7 @@ function BaseSelectionList({
showTooltip={showTooltip}
canSelectMultiple={canSelectMultiple}
onSelectRow={() => selectRow(item, true)}
+ disableIsFocusStyle={disableInitialFocusOptionStyle}
onDismissError={onDismissError}
shouldPreventDefaultFocusOnSelectRow={shouldPreventDefaultFocusOnSelectRow}
keyForList={item.keyForList}
@@ -393,9 +398,10 @@ function BaseSelectionList({
maxIndex={flattenedSections.allOptions.length - 1}
onFocusedIndexChanged={updateAndScrollToFocusedIndex}
>
+ {/* */}
{({safeAreaPaddingBottomStyle}) => (
-
+
{shouldShowTextInput && (
{(triggerKYCFlow, buttonRef) => (
{}}: SplashScreenHiderProps) {
+function SplashScreenHider({onHide = () => {}}: SplashScreenHiderProps): SplashScreenHiderReturnType {
const styles = useThemeStyles();
const logoSizeRatio = BootSplash.logoSizeRatio || 1;
const navigationBarHeight = BootSplash.navigationBarHeight || 0;
diff --git a/src/components/SplashScreenHider/index.tsx b/src/components/SplashScreenHider/index.tsx
index d3f5c52c1e3e..5710055b80ed 100644
--- a/src/components/SplashScreenHider/index.tsx
+++ b/src/components/SplashScreenHider/index.tsx
@@ -1,8 +1,8 @@
import {useEffect} from 'react';
import BootSplash from '@libs/BootSplash';
-import type SplashScreenHiderProps from './types';
+import type {SplashScreenHiderProps, SplashScreenHiderReturnType} from './types';
-function SplashScreenHider({onHide = () => {}}: SplashScreenHiderProps) {
+function SplashScreenHider({onHide = () => {}}: SplashScreenHiderProps): SplashScreenHiderReturnType {
useEffect(() => {
BootSplash.hide().then(() => onHide());
}, [onHide]);
diff --git a/src/components/SplashScreenHider/types.ts b/src/components/SplashScreenHider/types.ts
index 4ea25da93290..ff7b2beb1520 100644
--- a/src/components/SplashScreenHider/types.ts
+++ b/src/components/SplashScreenHider/types.ts
@@ -1,6 +1,10 @@
+import {ReactNode} from 'react';
+
type SplashScreenHiderProps = {
/** Splash screen has been hidden */
onHide: () => void;
};
-export default SplashScreenHiderProps;
+type SplashScreenHiderReturnType = ReactNode;
+
+export type {SplashScreenHiderProps, SplashScreenHiderReturnType};
diff --git a/src/components/StatePicker/StateSelectorModal.js b/src/components/StatePicker/StateSelectorModal.js
index 908bb5eb5b2a..003211478529 100644
--- a/src/components/StatePicker/StateSelectorModal.js
+++ b/src/components/StatePicker/StateSelectorModal.js
@@ -7,9 +7,9 @@ import Modal from '@components/Modal';
import ScreenWrapper from '@components/ScreenWrapper';
import SelectionList from '@components/SelectionList';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import searchCountryOptions from '@libs/searchCountryOptions';
import StringUtils from '@libs/StringUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
const propTypes = {
diff --git a/src/components/StatePicker/index.js b/src/components/StatePicker/index.js
index d9c137ac9dc1..6fa60fbba947 100644
--- a/src/components/StatePicker/index.js
+++ b/src/components/StatePicker/index.js
@@ -7,7 +7,7 @@ import FormHelpMessage from '@components/FormHelpMessage';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import refPropTypes from '@components/refPropTypes';
import useLocalize from '@hooks/useLocalize';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import StateSelectorModal from './StateSelectorModal';
const propTypes = {
diff --git a/src/components/SubscriptAvatar.tsx b/src/components/SubscriptAvatar.tsx
index 7fa2900eaffb..9a0aa934ff1b 100644
--- a/src/components/SubscriptAvatar.tsx
+++ b/src/components/SubscriptAvatar.tsx
@@ -1,10 +1,10 @@
import React, {memo} from 'react';
import {View} from 'react-native';
import {ValueOf} from 'type-fest';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import type {AvatarSource} from '@libs/UserUtils';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import {AvatarType} from '@src/types/onyx/OnyxCommon';
import Avatar from './Avatar';
diff --git a/src/components/Switch.tsx b/src/components/Switch.tsx
index 5195c0040add..fd9d9ae315ff 100644
--- a/src/components/Switch.tsx
+++ b/src/components/Switch.tsx
@@ -1,7 +1,7 @@
import React, {useEffect, useRef} from 'react';
import {Animated} from 'react-native';
+import useThemeStyles from '@hooks/useThemeStyles';
import useNativeDriver from '@libs/useNativeDriver';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import PressableWithFeedback from './Pressable/PressableWithFeedback';
diff --git a/src/components/TabSelector/TabIcon.js b/src/components/TabSelector/TabIcon.js
index 76217b69e9f3..d96ae19897f4 100644
--- a/src/components/TabSelector/TabIcon.js
+++ b/src/components/TabSelector/TabIcon.js
@@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
import React from 'react';
import {Animated, StyleSheet, View} from 'react-native';
import Icon from '@components/Icon';
-import useTheme from '@styles/themes/useTheme';
+import useTheme from '@hooks/useTheme';
const propTypes = {
/** Icon to display on tab */
diff --git a/src/components/TabSelector/TabLabel.js b/src/components/TabSelector/TabLabel.js
index 5dc050731fc5..fdf204011152 100644
--- a/src/components/TabSelector/TabLabel.js
+++ b/src/components/TabSelector/TabLabel.js
@@ -1,7 +1,7 @@
import PropTypes from 'prop-types';
import React from 'react';
import {Animated, StyleSheet, Text, View} from 'react-native';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
const propTypes = {
/** Title of the tab */
diff --git a/src/components/TabSelector/TabSelector.js b/src/components/TabSelector/TabSelector.js
index e968226e83fa..444bb62263d9 100644
--- a/src/components/TabSelector/TabSelector.js
+++ b/src/components/TabSelector/TabSelector.js
@@ -4,8 +4,8 @@ import {View} from 'react-native';
import _ from 'underscore';
import * as Expensicons from '@components/Icon/Expensicons';
import useLocalize from '@hooks/useLocalize';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
import TabSelectorItem from './TabSelectorItem';
diff --git a/src/components/TabSelector/TabSelectorItem.js b/src/components/TabSelector/TabSelectorItem.js
index 116b5db02d2c..88aa98766fae 100644
--- a/src/components/TabSelector/TabSelectorItem.js
+++ b/src/components/TabSelector/TabSelectorItem.js
@@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
import React from 'react';
import {Animated, StyleSheet} from 'react-native';
import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import TabIcon from './TabIcon';
import TabLabel from './TabLabel';
diff --git a/src/components/TagPicker/index.js b/src/components/TagPicker/index.js
index 334ca3e38370..cb3d9bf260e6 100644
--- a/src/components/TagPicker/index.js
+++ b/src/components/TagPicker/index.js
@@ -4,10 +4,10 @@ import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import OptionsSelector from '@components/OptionsSelector';
import useLocalize from '@hooks/useLocalize';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as PolicyUtils from '@libs/PolicyUtils';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import {defaultProps, propTypes} from './tagPickerPropTypes';
diff --git a/src/components/TaskHeaderActionButton.tsx b/src/components/TaskHeaderActionButton.tsx
index 5f8461d8e324..b1a32b356ae1 100644
--- a/src/components/TaskHeaderActionButton.tsx
+++ b/src/components/TaskHeaderActionButton.tsx
@@ -2,8 +2,8 @@ import React from 'react';
import {View} from 'react-native';
import {OnyxEntry, withOnyx} from 'react-native-onyx';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as ReportUtils from '@libs/ReportUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Session from '@userActions/Session';
import * as Task from '@userActions/Task';
import ONYXKEYS from '@src/ONYXKEYS';
@@ -13,6 +13,9 @@ import Button from './Button';
type TaskHeaderActionButtonOnyxProps = {
/** Current user session */
session: OnyxEntry;
+
+ /** The policy of root parent report */
+ policy: OnyxEntry;
};
type TaskHeaderActionButtonProps = TaskHeaderActionButtonOnyxProps & {
@@ -20,7 +23,7 @@ type TaskHeaderActionButtonProps = TaskHeaderActionButtonOnyxProps & {
report: OnyxTypes.Report;
};
-function TaskHeaderActionButton({report, session}: TaskHeaderActionButtonProps) {
+function TaskHeaderActionButton({report, session, policy}: TaskHeaderActionButtonProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
@@ -28,7 +31,7 @@ function TaskHeaderActionButton({report, session}: TaskHeaderActionButtonProps)
diff --git a/src/pages/home/report/ReportActionCompose/SendButton.js b/src/pages/home/report/ReportActionCompose/SendButton.js
index fde40998fa8c..d0b0453ace2f 100644
--- a/src/pages/home/report/ReportActionCompose/SendButton.js
+++ b/src/pages/home/report/ReportActionCompose/SendButton.js
@@ -7,8 +7,8 @@ import * as Expensicons from '@components/Icon/Expensicons';
import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
import Tooltip from '@components/Tooltip';
import useLocalize from '@hooks/useLocalize';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
const propTypes = {
diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js
index 3e4eebffe48f..d4731d3b929b 100644
--- a/src/pages/home/report/ReportActionItem.js
+++ b/src/pages/home/report/ReportActionItem.js
@@ -32,6 +32,9 @@ import UnreadActionIndicator from '@components/UnreadActionIndicator';
import withLocalize from '@components/withLocalize';
import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions';
import usePrevious from '@hooks/usePrevious';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import ControlSelection from '@libs/ControlSelection';
import * as CurrencyUtils from '@libs/CurrencyUtils';
@@ -46,9 +49,6 @@ import SelectionScraper from '@libs/SelectionScraper';
import userWalletPropTypes from '@pages/EnablePayments/userWalletPropTypes';
import {ReactionListContext} from '@pages/home/ReportScreenContext';
import reportPropTypes from '@pages/reportPropTypes';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import * as BankAccounts from '@userActions/BankAccounts';
import * as EmojiPickerAction from '@userActions/EmojiPickerAction';
import * as store from '@userActions/ReimbursementAccount/store';
@@ -358,18 +358,14 @@ function ReportActionItem(props) {
props.action.actionName === CONST.REPORT.ACTIONS.TYPE.TASKCANCELLED ||
props.action.actionName === CONST.REPORT.ACTIONS.TYPE.TASKREOPENED
) {
- children = (
-
- );
+ children = ;
} else if (ReportActionsUtils.isCreatedTaskReportAction(props.action)) {
children = (
{
+ validateCommentMaxLength(draft);
+ }, [draft, validateCommentMaxLength]);
+
return (
<>
@@ -470,11 +475,7 @@ function ReportActionItemMessageEdit(props) {
- setHasExceededMaxCommentLength(hasExceeded)}
- />
+
>
);
}
diff --git a/src/pages/home/report/ReportActionItemParentAction.js b/src/pages/home/report/ReportActionItemParentAction.js
index 4a125d1d5633..c11200ccc4db 100644
--- a/src/pages/home/report/ReportActionItemParentAction.js
+++ b/src/pages/home/report/ReportActionItemParentAction.js
@@ -6,11 +6,11 @@ import {withOnyx} from 'react-native-onyx';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import withLocalize from '@components/withLocalize';
import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import reportPropTypes from '@pages/reportPropTypes';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Report from '@userActions/Report';
import ONYXKEYS from '@src/ONYXKEYS';
import AnimatedEmptyStateBackground from './AnimatedEmptyStateBackground';
diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js
index 2c4df44d8b57..81827073aa49 100644
--- a/src/pages/home/report/ReportActionItemSingle.js
+++ b/src/pages/home/report/ReportActionItemSingle.js
@@ -13,6 +13,9 @@ import Text from '@components/Text';
import Tooltip from '@components/Tooltip';
import UserDetailsTooltip from '@components/UserDetailsTooltip';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import ControlSelection from '@libs/ControlSelection';
import DateUtils from '@libs/DateUtils';
import Navigation from '@libs/Navigation/Navigation';
@@ -20,9 +23,6 @@ import * as ReportUtils from '@libs/ReportUtils';
import * as UserUtils from '@libs/UserUtils';
import reportPropTypes from '@pages/reportPropTypes';
import stylePropTypes from '@styles/stylePropTypes';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import ReportActionItemDate from './ReportActionItemDate';
diff --git a/src/pages/home/report/ReportActionItemThread.js b/src/pages/home/report/ReportActionItemThread.tsx
similarity index 58%
rename from src/pages/home/report/ReportActionItemThread.js
rename to src/pages/home/report/ReportActionItemThread.tsx
index a3da95147020..e38021cf6ec1 100644
--- a/src/pages/home/report/ReportActionItemThread.js
+++ b/src/pages/home/report/ReportActionItemThread.tsx
@@ -1,62 +1,59 @@
-import PropTypes from 'prop-types';
import React from 'react';
import {Text, View} from 'react-native';
-import avatarPropTypes from '@components/avatarPropTypes';
import MultipleAvatars from '@components/MultipleAvatars';
import PressableWithSecondaryInteraction from '@components/PressableWithSecondaryInteraction';
-import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
-import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions';
-import compose from '@libs/compose';
-import useThemeStyles from '@styles/useThemeStyles';
+import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as Report from '@userActions/Report';
import CONST from '@src/CONST';
+import type {Icon} from '@src/types/onyx/OnyxCommon';
-const propTypes = {
+type ReportActionItemThreadProps = {
/** List of participant icons for the thread */
- icons: PropTypes.arrayOf(avatarPropTypes).isRequired,
+ icons: Icon[];
/** Number of comments under the thread */
- numberOfReplies: PropTypes.number.isRequired,
+ numberOfReplies: number;
/** Time of the most recent reply */
- mostRecentReply: PropTypes.string.isRequired,
+ mostRecentReply: string;
/** ID of child thread report */
- childReportID: PropTypes.string.isRequired,
+ childReportID: string;
/** Whether the thread item / message is being hovered */
- isHovered: PropTypes.bool.isRequired,
+ isHovered: boolean;
/** The function that should be called when the thread is LongPressed or right-clicked */
- onSecondaryInteraction: PropTypes.func.isRequired,
-
- ...withLocalizePropTypes,
- ...windowDimensionsPropTypes,
+ onSecondaryInteraction: () => void;
};
-function ReportActionItemThread(props) {
+function ReportActionItemThread({numberOfReplies, icons, mostRecentReply, childReportID, isHovered, onSecondaryInteraction}: ReportActionItemThreadProps) {
const styles = useThemeStyles();
- const numberOfRepliesText = props.numberOfReplies > CONST.MAX_THREAD_REPLIES_PREVIEW ? `${CONST.MAX_THREAD_REPLIES_PREVIEW}+` : `${props.numberOfReplies}`;
- const replyText = props.numberOfReplies === 1 ? props.translate('threads.reply') : props.translate('threads.replies');
- const timeStamp = props.datetimeToCalendarTime(props.mostRecentReply, false);
+ const {translate, datetimeToCalendarTime} = useLocalize();
+
+ const numberOfRepliesText = numberOfReplies > CONST.MAX_THREAD_REPLIES_PREVIEW ? `${CONST.MAX_THREAD_REPLIES_PREVIEW}+` : `${numberOfReplies}`;
+ const replyText = numberOfReplies === 1 ? translate('threads.reply') : translate('threads.replies');
+
+ const timeStamp = datetimeToCalendarTime(mostRecentReply, false);
return (
{
- Report.navigateToAndOpenChildReport(props.childReportID);
+ Report.navigateToAndOpenChildReport(childReportID);
}}
role={CONST.ROLE.BUTTON}
- accessibilityLabel={`${props.numberOfReplies} ${replyText}`}
- onSecondaryInteraction={props.onSecondaryInteraction}
+ accessibilityLabel={`${numberOfReplies} ${replyText}`}
+ onSecondaryInteraction={onSecondaryInteraction}
>
@@ -80,7 +77,6 @@ function ReportActionItemThread(props) {
);
}
-ReportActionItemThread.propTypes = propTypes;
ReportActionItemThread.displayName = 'ReportActionItemThread';
-export default compose(withLocalize, withWindowDimensions)(ReportActionItemThread);
+export default ReportActionItemThread;
diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js
index 55995a8059e7..e2ae7b947fcc 100644
--- a/src/pages/home/report/ReportActionsList.js
+++ b/src/pages/home/report/ReportActionsList.js
@@ -12,12 +12,13 @@ import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withW
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import useReportScrollManager from '@hooks/useReportScrollManager';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import DateUtils from '@libs/DateUtils';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
+import Visibility from '@libs/Visibility';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import * as Report from '@userActions/Report';
import CONST from '@src/CONST';
@@ -190,7 +191,7 @@ function ReportActionsList({
}
if (ReportUtils.isUnread(report)) {
- if (scrollingVerticalOffset.current < MSG_VISIBLE_THRESHOLD) {
+ if (Visibility.isVisible() && scrollingVerticalOffset.current < MSG_VISIBLE_THRESHOLD) {
Report.readNewestAction(report.reportID);
} else {
readActionSkipped.current = true;
diff --git a/src/pages/home/report/ReportDropUI.js b/src/pages/home/report/ReportDropUI.js
index 2f4c81313ec2..c1c3b8e506ab 100644
--- a/src/pages/home/report/ReportDropUI.js
+++ b/src/pages/home/report/ReportDropUI.js
@@ -6,7 +6,7 @@ import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
const propTypes = {
/** Callback to execute when a file is dropped. */
diff --git a/src/pages/home/report/ReportFooter.js b/src/pages/home/report/ReportFooter.js
index e5dd5da19ad5..48bfd5d18bcc 100644
--- a/src/pages/home/report/ReportFooter.js
+++ b/src/pages/home/report/ReportFooter.js
@@ -9,10 +9,10 @@ import participantPropTypes from '@components/participantPropTypes';
import SwipeableView from '@components/SwipeableView';
import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions';
import useNetwork from '@hooks/useNetwork';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import * as ReportUtils from '@libs/ReportUtils';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import * as Session from '@userActions/Session';
import CONST from '@src/CONST';
diff --git a/src/pages/home/report/ReportTypingIndicator.js b/src/pages/home/report/ReportTypingIndicator.js
index 3a2c611ac358..74778b364db1 100755
--- a/src/pages/home/report/ReportTypingIndicator.js
+++ b/src/pages/home/report/ReportTypingIndicator.js
@@ -7,8 +7,8 @@ import {withNetwork} from '@components/OnyxProvider';
import Text from '@components/Text';
import TextWithEllipsis from '@components/TextWithEllipsis';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
-import useThemeStyles from '@styles/useThemeStyles';
import * as PersonalDetails from '@userActions/PersonalDetails';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/home/report/comment/AttachmentCommentFragment.js b/src/pages/home/report/comment/AttachmentCommentFragment.js
index ec75edd18a35..30c131b57060 100644
--- a/src/pages/home/report/comment/AttachmentCommentFragment.js
+++ b/src/pages/home/report/comment/AttachmentCommentFragment.js
@@ -1,8 +1,8 @@
import PropTypes from 'prop-types';
import React from 'react';
import {View} from 'react-native';
+import useThemeStyles from '@hooks/useThemeStyles';
import reportActionSourcePropType from '@pages/home/report/reportActionSourcePropType';
-import useThemeStyles from '@styles/useThemeStyles';
import RenderCommentHTML from './RenderCommentHTML';
const propTypes = {
diff --git a/src/pages/home/report/comment/TextCommentFragment.js b/src/pages/home/report/comment/TextCommentFragment.js
index 403c470dfc9d..3d6482344450 100644
--- a/src/pages/home/report/comment/TextCommentFragment.js
+++ b/src/pages/home/report/comment/TextCommentFragment.js
@@ -5,15 +5,14 @@ import Text from '@components/Text';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions';
import ZeroWidthView from '@components/ZeroWidthView';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import convertToLTR from '@libs/convertToLTR';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import * as EmojiUtils from '@libs/EmojiUtils';
import reportActionFragmentPropTypes from '@pages/home/report/reportActionFragmentPropTypes';
import reportActionSourcePropType from '@pages/home/report/reportActionSourcePropType';
-import editedLabelStyles from '@styles/editedLabelStyles';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import RenderCommentHTML from './RenderCommentHTML';
@@ -103,7 +102,7 @@ function TextCommentFragment(props) {
{props.translate('reportActionCompose.edited')}
diff --git a/src/pages/home/sidebar/AvatarWithOptionalStatus.js b/src/pages/home/sidebar/AvatarWithOptionalStatus.js
index 20f9447f52ce..0bad9e845e9c 100644
--- a/src/pages/home/sidebar/AvatarWithOptionalStatus.js
+++ b/src/pages/home/sidebar/AvatarWithOptionalStatus.js
@@ -4,9 +4,10 @@ import React, {useCallback} from 'react';
import {View} from 'react-native';
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
import Text from '@components/Text';
+import Tooltip from '@components/Tooltip';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import PressableAvatarWithIndicator from './PressableAvatarWithIndicator';
@@ -46,14 +47,16 @@ function AvatarWithOptionalStatus({emojiStatus, isCreateMenuOpen}) {
onPress={showStatusPage}
style={styles.flex1}
>
-
-
- {emojiStatus}
-
-
+
+
+
+ {emojiStatus}
+
+
+
diff --git a/src/pages/home/sidebar/SidebarLinks.js b/src/pages/home/sidebar/SidebarLinks.js
index 6f860010e9d7..eb4f9000d2b4 100644
--- a/src/pages/home/sidebar/SidebarLinks.js
+++ b/src/pages/home/sidebar/SidebarLinks.js
@@ -12,6 +12,9 @@ import OptionsListSkeletonView from '@components/OptionsListSkeletonView';
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
import Tooltip from '@components/Tooltip';
import useLocalize from '@hooks/useLocalize';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import KeyboardShortcut from '@libs/KeyboardShortcut';
import Navigation from '@libs/Navigation/Navigation';
@@ -19,9 +22,6 @@ import onyxSubscribe from '@libs/onyxSubscribe';
import SidebarUtils from '@libs/SidebarUtils';
import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu';
import safeAreaInsetPropTypes from '@pages/safeAreaInsetPropTypes';
-import useTheme from '@styles/themes/useTheme';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import * as App from '@userActions/App';
import * as Session from '@userActions/Session';
diff --git a/src/pages/home/sidebar/SidebarLinksData.js b/src/pages/home/sidebar/SidebarLinksData.js
index 97f119bbd2e8..dbc77a41817b 100644
--- a/src/pages/home/sidebar/SidebarLinksData.js
+++ b/src/pages/home/sidebar/SidebarLinksData.js
@@ -10,10 +10,10 @@ import {withNetwork} from '@components/OnyxProvider';
import withCurrentReportID from '@components/withCurrentReportID';
import withNavigationFocus from '@components/withNavigationFocus';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import SidebarUtils from '@libs/SidebarUtils';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import SidebarLinks, {basePropTypes} from './SidebarLinks';
diff --git a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js
index 5b7a126a4655..a6853316b582 100644
--- a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js
+++ b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js
@@ -1,10 +1,10 @@
import React, {useEffect} from 'react';
import {View} from 'react-native';
import ScreenWrapper from '@components/ScreenWrapper';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as Browser from '@libs/Browser';
import Performance from '@libs/Performance';
import SidebarLinksData from '@pages/home/sidebar/SidebarLinksData';
-import useThemeStyles from '@styles/useThemeStyles';
import Timing from '@userActions/Timing';
import CONST from '@src/CONST';
import sidebarPropTypes from './sidebarPropTypes';
diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js
index 1da45bc71777..65b79ed5af78 100644
--- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js
+++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js
@@ -11,10 +11,10 @@ import withNavigation from '@components/withNavigation';
import withNavigationFocus from '@components/withNavigationFocus';
import withWindowDimensions from '@components/withWindowDimensions';
import usePrevious from '@hooks/usePrevious';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
import * as ReportUtils from '@libs/ReportUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import * as App from '@userActions/App';
import * as IOU from '@userActions/IOU';
import * as Policy from '@userActions/Policy';
diff --git a/src/pages/home/sidebar/SignInButton.js b/src/pages/home/sidebar/SignInButton.js
index f820298b0a22..9edcc9584dbd 100644
--- a/src/pages/home/sidebar/SignInButton.js
+++ b/src/pages/home/sidebar/SignInButton.js
@@ -4,7 +4,7 @@ import {View} from 'react-native';
import Button from '@components/Button';
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
import useLocalize from '@hooks/useLocalize';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as Session from '@userActions/Session';
import CONST from '@src/CONST';
diff --git a/src/pages/iou/MoneyRequestCategoryPage.js b/src/pages/iou/MoneyRequestCategoryPage.js
index 7f3bf7985ba6..ceb2152d2b49 100644
--- a/src/pages/iou/MoneyRequestCategoryPage.js
+++ b/src/pages/iou/MoneyRequestCategoryPage.js
@@ -7,10 +7,10 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
diff --git a/src/pages/iou/MoneyRequestDatePage.js b/src/pages/iou/MoneyRequestDatePage.js
index 8121bb381f32..b7d1c4002da1 100644
--- a/src/pages/iou/MoneyRequestDatePage.js
+++ b/src/pages/iou/MoneyRequestDatePage.js
@@ -8,9 +8,9 @@ import FormProvider from '@components/Form/FormProvider';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as MoneyRequestUtils from '@libs/MoneyRequestUtils';
import Navigation from '@libs/Navigation/Navigation';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/iou/MoneyRequestDescriptionPage.js b/src/pages/iou/MoneyRequestDescriptionPage.js
index 2d3e7d887e60..fe3100b8c3bd 100644
--- a/src/pages/iou/MoneyRequestDescriptionPage.js
+++ b/src/pages/iou/MoneyRequestDescriptionPage.js
@@ -11,12 +11,12 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import TextInput from '@components/TextInput';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as IOU from '@libs/actions/IOU';
import * as Browser from '@libs/Browser';
import * as MoneyRequestUtils from '@libs/MoneyRequestUtils';
import Navigation from '@libs/Navigation/Navigation';
import updateMultilineInputRange from '@libs/updateMultilineInputRange';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
diff --git a/src/pages/iou/MoneyRequestMerchantPage.js b/src/pages/iou/MoneyRequestMerchantPage.js
index be57f0b6c534..bf799cd0957b 100644
--- a/src/pages/iou/MoneyRequestMerchantPage.js
+++ b/src/pages/iou/MoneyRequestMerchantPage.js
@@ -11,8 +11,8 @@ import ScreenWrapper from '@components/ScreenWrapper';
import TextInput from '@components/TextInput';
import useAutoFocusInput from '@hooks/useAutoFocusInput';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/iou/MoneyRequestSelectorPage.js b/src/pages/iou/MoneyRequestSelectorPage.js
index ee4ee88d7008..e367709cb010 100644
--- a/src/pages/iou/MoneyRequestSelectorPage.js
+++ b/src/pages/iou/MoneyRequestSelectorPage.js
@@ -11,6 +11,7 @@ import ScreenWrapper from '@components/ScreenWrapper';
import TabSelector from '@components/TabSelector/TabSelector';
import useLocalize from '@hooks/useLocalize';
import usePrevious from '@hooks/usePrevious';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import * as IOUUtils from '@libs/IOUUtils';
@@ -19,7 +20,6 @@ import OnyxTabNavigator, {TopTab} from '@libs/Navigation/OnyxTabNavigator';
import * as ReportUtils from '@libs/ReportUtils';
import withReportOrNotFound from '@pages/home/report/withReportOrNotFound';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/iou/MoneyRequestTagPage.js b/src/pages/iou/MoneyRequestTagPage.js
index d16a7aa6679c..60e40d665580 100644
--- a/src/pages/iou/MoneyRequestTagPage.js
+++ b/src/pages/iou/MoneyRequestTagPage.js
@@ -9,11 +9,11 @@ import TagPicker from '@components/TagPicker';
import tagPropTypes from '@components/tagPropTypes';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
import * as PolicyUtils from '@libs/PolicyUtils';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
diff --git a/src/pages/iou/ReceiptDropUI.js b/src/pages/iou/ReceiptDropUI.js
index 18ad31e036e4..67330860a052 100644
--- a/src/pages/iou/ReceiptDropUI.js
+++ b/src/pages/iou/ReceiptDropUI.js
@@ -4,7 +4,7 @@ import {Text, View} from 'react-native';
import ReceiptUpload from '@assets/images/receipt-upload.svg';
import DragAndDropConsumer from '@components/DragAndDrop/Consumer';
import useLocalize from '@hooks/useLocalize';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
const propTypes = {
diff --git a/src/pages/iou/ReceiptSelector/index.js b/src/pages/iou/ReceiptSelector/index.js
index e9e734ddf2f8..adbfc0618de7 100644
--- a/src/pages/iou/ReceiptSelector/index.js
+++ b/src/pages/iou/ReceiptSelector/index.js
@@ -15,6 +15,8 @@ import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as Browser from '@libs/Browser';
import * as FileUtils from '@libs/fileDownload/FileUtils';
@@ -22,8 +24,6 @@ import Navigation from '@libs/Navigation/Navigation';
import {iouDefaultProps, iouPropTypes} from '@pages/iou/propTypes';
import ReceiptDropUI from '@pages/iou/ReceiptDropUI';
import reportPropTypes from '@pages/reportPropTypes';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/iou/ReceiptSelector/index.native.js b/src/pages/iou/ReceiptSelector/index.native.js
index 0073725a2c50..109e7ebdaa71 100644
--- a/src/pages/iou/ReceiptSelector/index.native.js
+++ b/src/pages/iou/ReceiptSelector/index.native.js
@@ -13,13 +13,13 @@ import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as FileUtils from '@libs/fileDownload/FileUtils';
import Log from '@libs/Log';
import Navigation from '@libs/Navigation/Navigation';
import {iouDefaultProps, iouPropTypes} from '@pages/iou/propTypes';
import reportPropTypes from '@pages/reportPropTypes';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/iou/SplitBillDetailsPage.js b/src/pages/iou/SplitBillDetailsPage.js
index dd9e7ce93cc6..94c2f1c31242 100644
--- a/src/pages/iou/SplitBillDetailsPage.js
+++ b/src/pages/iou/SplitBillDetailsPage.js
@@ -11,6 +11,7 @@ import MoneyRequestHeaderStatusBar from '@components/MoneyRequestHeaderStatusBar
import ScreenWrapper from '@components/ScreenWrapper';
import transactionPropTypes from '@components/transactionPropTypes';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as ReportUtils from '@libs/ReportUtils';
@@ -19,7 +20,6 @@ import reportActionPropTypes from '@pages/home/report/reportActionPropTypes';
import withReportAndReportActionOrNotFound from '@pages/home/report/withReportAndReportActionOrNotFound';
import personalDetailsPropType from '@pages/personalDetailsPropType';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/iou/WaypointEditor.js b/src/pages/iou/WaypointEditor.js
index 3c0d8802c5d0..e8d3c8520ca8 100644
--- a/src/pages/iou/WaypointEditor.js
+++ b/src/pages/iou/WaypointEditor.js
@@ -15,11 +15,11 @@ import ScreenWrapper from '@components/ScreenWrapper';
import transactionPropTypes from '@components/transactionPropTypes';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as ErrorUtils from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as ValidationUtils from '@libs/ValidationUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Transaction from '@userActions/Transaction';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/iou/request/IOURequestStartPage.js b/src/pages/iou/request/IOURequestStartPage.js
index c24e4f4e9e5f..6572e154ee14 100644
--- a/src/pages/iou/request/IOURequestStartPage.js
+++ b/src/pages/iou/request/IOURequestStartPage.js
@@ -13,6 +13,7 @@ import TabSelector from '@components/TabSelector/TabSelector';
import transactionPropTypes from '@components/transactionPropTypes';
import useLocalize from '@hooks/useLocalize';
import usePrevious from '@hooks/usePrevious';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import * as IOUUtils from '@libs/IOUUtils';
import Navigation from '@libs/Navigation/Navigation';
@@ -20,7 +21,6 @@ import OnyxTabNavigator, {TopTab} from '@libs/Navigation/OnyxTabNavigator';
import * as ReportUtils from '@libs/ReportUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js
index daa874488980..8d7d5cfceb77 100644
--- a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js
+++ b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js
@@ -10,6 +10,7 @@ import OptionsSelector from '@components/OptionsSelector';
import refPropTypes from '@components/refPropTypes';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import useNetwork from '@hooks/useNetwork';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as Report from '@libs/actions/Report';
import * as Browser from '@libs/Browser';
import compose from '@libs/compose';
@@ -17,7 +18,6 @@ import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as ReportUtils from '@libs/ReportUtils';
import personalDetailsPropType from '@pages/personalDetailsPropType';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/iou/request/step/IOURequestStepCategory.js b/src/pages/iou/request/step/IOURequestStepCategory.js
index 7614683f1e1e..2218262e1877 100644
--- a/src/pages/iou/request/step/IOURequestStepCategory.js
+++ b/src/pages/iou/request/step/IOURequestStepCategory.js
@@ -3,10 +3,10 @@ import CategoryPicker from '@components/CategoryPicker';
import Text from '@components/Text';
import transactionPropTypes from '@components/transactionPropTypes';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import ROUTES from '@src/ROUTES';
import IOURequestStepRoutePropTypes from './IOURequestStepRoutePropTypes';
diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.js b/src/pages/iou/request/step/IOURequestStepConfirmation.js
index cb90103c6ffc..6d5375c7a1bb 100644
--- a/src/pages/iou/request/step/IOURequestStepConfirmation.js
+++ b/src/pages/iou/request/step/IOURequestStepConfirmation.js
@@ -11,6 +11,7 @@ import transactionPropTypes from '@components/transactionPropTypes';
import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import compose from '@libs/compose';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
@@ -22,7 +23,6 @@ import * as TransactionUtils from '@libs/TransactionUtils';
import personalDetailsPropType from '@pages/personalDetailsPropType';
import reportPropTypes from '@pages/reportPropTypes';
import {policyPropTypes} from '@pages/workspace/withPolicy';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import * as Policy from '@userActions/Policy';
import CONST from '@src/CONST';
@@ -80,13 +80,10 @@ function IOURequestStepConfirmation({
const headerTitle = iouType === CONST.IOU.TYPE.SPLIT ? translate('iou.split') : translate(TransactionUtils.getHeaderTitleTranslationKey(transaction));
const participants = useMemo(
() =>
- _.chain(transaction.participants)
- .map((participant) => {
- const isPolicyExpenseChat = lodashGet(participant, 'isPolicyExpenseChat', false);
- return isPolicyExpenseChat ? OptionsListUtils.getPolicyExpenseReportOption(participant) : OptionsListUtils.getParticipantsOption(participant, personalDetails);
- })
- .filter((participant) => !!participant.login || !!participant.text)
- .value(),
+ _.map(transaction.participants, (participant) => {
+ const isPolicyExpenseChat = lodashGet(participant, 'isPolicyExpenseChat', false);
+ return isPolicyExpenseChat ? OptionsListUtils.getPolicyExpenseReportOption(participant) : OptionsListUtils.getParticipantsOption(participant, personalDetails);
+ }),
[transaction.participants, personalDetails],
);
const isPolicyExpenseChat = useMemo(() => ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(report)), [report]);
diff --git a/src/pages/iou/request/step/IOURequestStepDate.js b/src/pages/iou/request/step/IOURequestStepDate.js
index 58c67b26052a..c90779af47ee 100644
--- a/src/pages/iou/request/step/IOURequestStepDate.js
+++ b/src/pages/iou/request/step/IOURequestStepDate.js
@@ -5,10 +5,10 @@ import DatePicker from '@components/DatePicker';
import FormProvider from '@components/Form/FormProvider';
import transactionPropTypes from '@components/transactionPropTypes';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import * as IOUUtils from '@libs/IOUUtils';
import Navigation from '@libs/Navigation/Navigation';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
diff --git a/src/pages/iou/request/step/IOURequestStepDescription.js b/src/pages/iou/request/step/IOURequestStepDescription.js
index 6cf9f86068a6..849f3276667e 100644
--- a/src/pages/iou/request/step/IOURequestStepDescription.js
+++ b/src/pages/iou/request/step/IOURequestStepDescription.js
@@ -7,11 +7,11 @@ import InputWrapperWithRef from '@components/Form/InputWrapper';
import TextInput from '@components/TextInput';
import transactionPropTypes from '@components/transactionPropTypes';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as Browser from '@libs/Browser';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
import updateMultilineInputRange from '@libs/updateMultilineInputRange';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/iou/request/step/IOURequestStepDistance.js b/src/pages/iou/request/step/IOURequestStepDistance.js
index 39e9d6f03afa..a4b1e97425ee 100644
--- a/src/pages/iou/request/step/IOURequestStepDistance.js
+++ b/src/pages/iou/request/step/IOURequestStepDistance.js
@@ -11,12 +11,12 @@ import transactionPropTypes from '@components/transactionPropTypes';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import usePrevious from '@hooks/usePrevious';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import * as ErrorUtils from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as TransactionUtils from '@libs/TransactionUtils';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import * as IOU from '@userActions/IOU';
import * as MapboxToken from '@userActions/MapboxToken';
@@ -51,9 +51,9 @@ function IOURequestStepDistance({
},
transaction,
}) {
+ const styles = useThemeStyles();
const {isOffline} = useNetwork();
const {translate} = useLocalize();
- const styles = useThemeStyles();
const [optimisticWaypoints, setOptimisticWaypoints] = useState(null);
const [hasError, setHasError] = useState(false);
diff --git a/src/pages/iou/request/step/IOURequestStepMerchant.js b/src/pages/iou/request/step/IOURequestStepMerchant.js
index 2dbb6f8473b9..3234b6046f31 100644
--- a/src/pages/iou/request/step/IOURequestStepMerchant.js
+++ b/src/pages/iou/request/step/IOURequestStepMerchant.js
@@ -7,9 +7,9 @@ import TextInput from '@components/TextInput';
import transactionPropTypes from '@components/transactionPropTypes';
import useAutoFocusInput from '@hooks/useAutoFocusInput';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.js b/src/pages/iou/request/step/IOURequestStepScan/index.js
index e4c50ff25b5c..c51727f0143b 100644
--- a/src/pages/iou/request/step/IOURequestStepScan/index.js
+++ b/src/pages/iou/request/step/IOURequestStepScan/index.js
@@ -13,6 +13,8 @@ import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as Browser from '@libs/Browser';
import compose from '@libs/compose';
@@ -24,8 +26,6 @@ import StepScreenDragAndDropWrapper from '@pages/iou/request/step/StepScreenDrag
import withFullTransactionOrNotFound from '@pages/iou/request/step/withFullTransactionOrNotFound';
import withWritableReportOrNotFound from '@pages/iou/request/step/withWritableReportOrNotFound';
import reportPropTypes from '@pages/reportPropTypes';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.js b/src/pages/iou/request/step/IOURequestStepScan/index.native.js
index e80fbbce0676..8745600924e8 100644
--- a/src/pages/iou/request/step/IOURequestStepScan/index.native.js
+++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.js
@@ -11,6 +11,8 @@ import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import * as FileUtils from '@libs/fileDownload/FileUtils';
import Log from '@libs/Log';
@@ -20,8 +22,6 @@ import StepScreenWrapper from '@pages/iou/request/step/StepScreenWrapper';
import withFullTransactionOrNotFound from '@pages/iou/request/step/withFullTransactionOrNotFound';
import withWritableReportOrNotFound from '@pages/iou/request/step/withWritableReportOrNotFound';
import reportPropTypes from '@pages/reportPropTypes';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
diff --git a/src/pages/iou/request/step/IOURequestStepTag.js b/src/pages/iou/request/step/IOURequestStepTag.js
index 12e5dc8f9dab..7e2ccbe1a9dd 100644
--- a/src/pages/iou/request/step/IOURequestStepTag.js
+++ b/src/pages/iou/request/step/IOURequestStepTag.js
@@ -6,11 +6,11 @@ import tagPropTypes from '@components/tagPropTypes';
import Text from '@components/Text';
import transactionPropTypes from '@components/transactionPropTypes';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
import * as PolicyUtils from '@libs/PolicyUtils';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
diff --git a/src/pages/iou/request/step/IOURequestStepWaypoint.js b/src/pages/iou/request/step/IOURequestStepWaypoint.js
index 7af58a7e0352..dc5b9f7d6275 100644
--- a/src/pages/iou/request/step/IOURequestStepWaypoint.js
+++ b/src/pages/iou/request/step/IOURequestStepWaypoint.js
@@ -16,12 +16,12 @@ import ScreenWrapper from '@components/ScreenWrapper';
import transactionPropTypes from '@components/transactionPropTypes';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import compose from '@libs/compose';
import * as ErrorUtils from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as ValidationUtils from '@libs/ValidationUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Transaction from '@userActions/Transaction';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/iou/request/step/StepScreenDragAndDropWrapper.js b/src/pages/iou/request/step/StepScreenDragAndDropWrapper.js
index a369775d7110..ceb0d5a44351 100644
--- a/src/pages/iou/request/step/StepScreenDragAndDropWrapper.js
+++ b/src/pages/iou/request/step/StepScreenDragAndDropWrapper.js
@@ -4,8 +4,8 @@ import {View} from 'react-native';
import DragAndDropProvider from '@components/DragAndDrop/Provider';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
-import useThemeStyles from '@styles/useThemeStyles';
const propTypes = {
/** The things to display inside the screenwrapper */
@@ -32,9 +32,10 @@ const defaultProps = {
};
function StepScreenDragAndDropWrapper({testID, headerTitle, onBackButtonPress, onEntryTransitionEnd, children, shouldShowWrapper}) {
- const [isDraggingOver, setIsDraggingOver] = useState(false);
const styles = useThemeStyles();
+ const [isDraggingOver, setIsDraggingOver] = useState(false);
+
if (!shouldShowWrapper) {
return children;
}
diff --git a/src/pages/iou/request/step/StepScreenWrapper.js b/src/pages/iou/request/step/StepScreenWrapper.js
index 5131a9c60dc6..eae542f0f6f9 100644
--- a/src/pages/iou/request/step/StepScreenWrapper.js
+++ b/src/pages/iou/request/step/StepScreenWrapper.js
@@ -4,8 +4,8 @@ import {View} from 'react-native';
import _ from 'underscore';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
-import useThemeStyles from '@styles/useThemeStyles';
const propTypes = {
/** The things to display inside the screenwrapper */
diff --git a/src/pages/iou/steps/MoneyRequestAmountForm.js b/src/pages/iou/steps/MoneyRequestAmountForm.js
index a8e56f7a41e8..c9075d896deb 100644
--- a/src/pages/iou/steps/MoneyRequestAmountForm.js
+++ b/src/pages/iou/steps/MoneyRequestAmountForm.js
@@ -9,13 +9,13 @@ import FormHelpMessage from '@components/FormHelpMessage';
import refPropTypes from '@components/refPropTypes';
import TextInputWithCurrencySymbol from '@components/TextInputWithCurrencySymbol';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as Browser from '@libs/Browser';
import * as CurrencyUtils from '@libs/CurrencyUtils';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import getOperatingSystem from '@libs/getOperatingSystem';
import * as MoneyRequestUtils from '@libs/MoneyRequestUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
const propTypes = {
diff --git a/src/pages/iou/steps/MoneyRequestConfirmPage.js b/src/pages/iou/steps/MoneyRequestConfirmPage.js
index 03ebb9bdb562..3476b2304875 100644
--- a/src/pages/iou/steps/MoneyRequestConfirmPage.js
+++ b/src/pages/iou/steps/MoneyRequestConfirmPage.js
@@ -13,6 +13,7 @@ import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultPro
import withLocalize from '@components/withLocalize';
import useInitialValue from '@hooks/useInitialValue';
import useNetwork from '@hooks/useNetwork';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import compose from '@libs/compose';
import * as FileUtils from '@libs/fileDownload/FileUtils';
@@ -22,7 +23,6 @@ import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as ReportUtils from '@libs/ReportUtils';
import {iouDefaultProps, iouPropTypes} from '@pages/iou/propTypes';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import * as Policy from '@userActions/Policy';
import CONST from '@src/CONST';
diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js
index d44c40db209f..7826643d4283 100644
--- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js
+++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js
@@ -10,13 +10,13 @@ import ScreenWrapper from '@components/ScreenWrapper';
import transactionPropTypes from '@components/transactionPropTypes';
import useInitialValue from '@hooks/useInitialValue';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import * as MoneyRequestUtils from '@libs/MoneyRequestUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as TransactionUtils from '@libs/TransactionUtils';
import {iouDefaultProps, iouPropTypes} from '@pages/iou/propTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js
index a8b4dae0facc..d8d644479270 100755
--- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js
+++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js
@@ -10,6 +10,7 @@ import OptionsSelector from '@components/OptionsSelector';
import refPropTypes from '@components/refPropTypes';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import useNetwork from '@hooks/useNetwork';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as Report from '@libs/actions/Report';
import * as Browser from '@libs/Browser';
import compose from '@libs/compose';
@@ -17,7 +18,6 @@ import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as ReportUtils from '@libs/ReportUtils';
import personalDetailsPropType from '@pages/personalDetailsPropType';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/iou/steps/NewRequestAmountPage.js b/src/pages/iou/steps/NewRequestAmountPage.js
index bf92c420e82c..569e2ce693ee 100644
--- a/src/pages/iou/steps/NewRequestAmountPage.js
+++ b/src/pages/iou/steps/NewRequestAmountPage.js
@@ -9,13 +9,13 @@ import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as CurrencyUtils from '@libs/CurrencyUtils';
import * as IOUUtils from '@libs/IOUUtils';
import * as MoneyRequestUtils from '@libs/MoneyRequestUtils';
import Navigation from '@libs/Navigation/Navigation';
import {iouDefaultProps, iouPropTypes} from '@pages/iou/propTypes';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/settings/AboutPage/AboutPage.js b/src/pages/settings/AboutPage/AboutPage.js
index c192abf84e43..16ae590cf86b 100644
--- a/src/pages/settings/AboutPage/AboutPage.js
+++ b/src/pages/settings/AboutPage/AboutPage.js
@@ -11,13 +11,13 @@ import Text from '@components/Text';
import TextLink from '@components/TextLink';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWaitForNavigation from '@hooks/useWaitForNavigation';
import compose from '@libs/compose';
import * as Environment from '@libs/Environment/Environment';
import Navigation from '@libs/Navigation/Navigation';
import {CONTEXT_MENU_TYPES} from '@pages/home/report/ContextMenu/ContextMenuActions';
import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Link from '@userActions/Link';
import * as Report from '@userActions/Report';
import CONST from '@src/CONST';
diff --git a/src/pages/settings/AppDownloadLinks.js b/src/pages/settings/AppDownloadLinks.js
index 182a25a121b5..471a38b039b2 100644
--- a/src/pages/settings/AppDownloadLinks.js
+++ b/src/pages/settings/AppDownloadLinks.js
@@ -7,11 +7,11 @@ import MenuItem from '@components/MenuItem';
import ScreenWrapper from '@components/ScreenWrapper';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
import {CONTEXT_MENU_TYPES} from '@pages/home/report/ContextMenu/ContextMenuActions';
import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Link from '@userActions/Link';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js
index 2af5b145caab..b10c0b2e4d2b 100755
--- a/src/pages/settings/InitialSettingsPage.js
+++ b/src/pages/settings/InitialSettingsPage.js
@@ -21,6 +21,8 @@ import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultPro
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import useLocalize from '@hooks/useLocalize';
import useSingleExecution from '@hooks/useSingleExecution';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWaitForNavigation from '@hooks/useWaitForNavigation';
import * as CardUtils from '@libs/CardUtils';
import compose from '@libs/compose';
@@ -34,8 +36,6 @@ import {CONTEXT_MENU_TYPES} from '@pages/home/report/ContextMenu/ContextMenuActi
import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu';
import policyMemberPropType from '@pages/policyMemberPropType';
import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimbursementAccountPropTypes';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Link from '@userActions/Link';
import * as PaymentMethods from '@userActions/PaymentMethods';
import * as Session from '@userActions/Session';
@@ -264,6 +264,16 @@ function InitialSettingsPage(props) {
Navigation.navigate(ROUTES.SETTINGS_ABOUT);
}),
},
+ {
+ translationKey: 'initialSettingsPage.goToExpensifyClassic',
+ icon: Expensicons.NewExpensify,
+ action: () => {
+ Link.openExternalLink(CONST.EXPENSIFY_INBOX_URL);
+ },
+ shouldShowRightIcon: true,
+ iconRight: Expensicons.NewWindow,
+ link: CONST.EXPENSIFY_INBOX_URL,
+ },
{
translationKey: 'initialSettingsPage.signOut',
icon: Expensicons.Exit,
diff --git a/src/pages/settings/Preferences/PreferencesPage.js b/src/pages/settings/Preferences/PreferencesPage.js
index 6fa3e2df3474..e98cd2b19c39 100755
--- a/src/pages/settings/Preferences/PreferencesPage.js
+++ b/src/pages/settings/Preferences/PreferencesPage.js
@@ -11,9 +11,9 @@ import TestToolMenu from '@components/TestToolMenu';
import Text from '@components/Text';
import useEnvironment from '@hooks/useEnvironment';
import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import * as User from '@userActions/User';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
@@ -24,6 +24,9 @@ const propTypes = {
/** The chat priority mode */
priorityMode: PropTypes.string,
+ /** The app's color theme */
+ preferredTheme: PropTypes.string,
+
/** The details about the user that is signed in */
user: PropTypes.shape({
/** Whether or not the user is subscribed to news updates */
@@ -33,6 +36,7 @@ const propTypes = {
const defaultProps = {
priorityMode: CONST.PRIORITY_MODE.DEFAULT,
+ preferredTheme: CONST.DEFAULT_THEME,
user: {},
};
@@ -80,6 +84,12 @@ function PreferencesPage(props) {
description={translate('languagePage.language')}
onPress={() => Navigation.navigate(ROUTES.SETTINGS_LANGUAGE)}
/>
+ Navigation.navigate(ROUTES.SETTINGS_THEME)}
+ />
{/* Enable additional test features in non-production environments */}
{!isProduction && (
@@ -102,4 +112,7 @@ export default withOnyx({
user: {
key: ONYXKEYS.USER,
},
+ preferredTheme: {
+ key: ONYXKEYS.PREFERRED_THEME,
+ },
})(PreferencesPage);
diff --git a/src/pages/settings/Preferences/PriorityModePage.js b/src/pages/settings/Preferences/PriorityModePage.js
index 0778394d2b28..2a9d9c5b0d9e 100644
--- a/src/pages/settings/Preferences/PriorityModePage.js
+++ b/src/pages/settings/Preferences/PriorityModePage.js
@@ -7,8 +7,8 @@ import ScreenWrapper from '@components/ScreenWrapper';
import SelectionList from '@components/SelectionList';
import Text from '@components/Text';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
-import useThemeStyles from '@styles/useThemeStyles';
import * as User from '@userActions/User';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/settings/Preferences/ThemePage.js b/src/pages/settings/Preferences/ThemePage.js
index f4acd10a7230..453bf0a72aa8 100644
--- a/src/pages/settings/Preferences/ThemePage.js
+++ b/src/pages/settings/Preferences/ThemePage.js
@@ -6,18 +6,15 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import SelectionList from '@components/SelectionList';
import Text from '@components/Text';
-import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
-import compose from '@libs/compose';
+import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
-import useThemeStyles from '@styles/useThemeStyles';
import * as User from '@userActions/User';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
const propTypes = {
- ...withLocalizePropTypes,
-
/** The theme of the app */
preferredTheme: PropTypes.string,
};
@@ -28,9 +25,10 @@ const defaultProps = {
function ThemePage(props) {
const styles = useThemeStyles();
- const localesToThemes = _.map(_.values(_.omit(CONST.THEME, 'DEFAULT')), (theme) => ({
+ const {translate} = useLocalize();
+ const localesToThemes = _.map(_.values(_.omit(CONST.THEME, 'DEFAULT', 'FALLBACK')), (theme) => ({
value: theme,
- text: props.translate(`themePage.themes.${theme}.label`),
+ text: translate(`themePage.themes.${theme}.label`),
keyForList: theme,
isSelected: (props.preferredTheme || CONST.THEME.DEFAULT) === theme,
}));
@@ -41,13 +39,13 @@ function ThemePage(props) {
testID={ThemePage.displayName}
>
Navigation.navigate(ROUTES.SETTINGS_PREFERENCES)}
onCloseButtonPress={() => Navigation.dismissModal(true)}
/>
- {props.translate('themePage.chooseThemeBelowOrSync')}
+ {translate('themePage.chooseThemeBelowOrSync')} {
+ User.updateDraftCustomStatus({clearAfter: DateUtils.combineDateAndTime(customClearAfter, v.dateTime)});
+ Navigation.goBack(ROUTES.SETTINGS_STATUS_CLEAR_AFTER);
+ };
+
+ const validate = useCallback((values) => {
+ const requiredFields = ['dateTime'];
+ const errors = ValidationUtils.getFieldRequiredErrors(values, requiredFields);
+ const dateError = ValidationUtils.getDatePassedError(values.dateTime);
+
+ if (values.dateTime && dateError) {
+ errors.dateTime = dateError;
+ }
+
+ return errors;
+ }, []);
+
+ return (
+
+ Navigation.goBack(ROUTES.SETTINGS_STATUS_CLEAR_AFTER)}
+ />
+
+
+
+
+ );
+}
+
+SetDatePage.propTypes = propTypes;
+SetDatePage.displayName = 'SetDatePage';
+
+export default compose(
+ withLocalize,
+ withOnyx({
+ privatePersonalDetails: {
+ key: ONYXKEYS.PRIVATE_PERSONAL_DETAILS,
+ },
+ customStatus: {
+ key: ONYXKEYS.CUSTOM_STATUS_DRAFT,
+ },
+ clearDateForm: {
+ key: `${ONYXKEYS.FORMS.SETTINGS_STATUS_CLEAR_DATE_FORM}Draft`,
+ },
+ }),
+)(SetDatePage);
diff --git a/src/pages/settings/Profile/CustomStatus/SetTimePage.js b/src/pages/settings/Profile/CustomStatus/SetTimePage.js
new file mode 100644
index 000000000000..3cdf739ac7b9
--- /dev/null
+++ b/src/pages/settings/Profile/CustomStatus/SetTimePage.js
@@ -0,0 +1,73 @@
+import lodashGet from 'lodash/get';
+import React from 'react';
+import {View} from 'react-native';
+import {withOnyx} from 'react-native-onyx';
+import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
+import HeaderWithBackButton from '@components/HeaderWithBackButton';
+import ScreenWrapper from '@components/ScreenWrapper';
+import TimePicker from '@components/TimePicker/TimePicker';
+import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails';
+import useThemeStyles from '@hooks/useThemeStyles';
+import * as User from '@libs/actions/User';
+import compose from '@libs/compose';
+import DateUtils from '@libs/DateUtils';
+import Navigation from '@libs/Navigation/Navigation';
+import ONYXKEYS from '@src/ONYXKEYS';
+import ROUTES from '@src/ROUTES';
+
+const propTypes = {
+ ...withLocalizePropTypes,
+};
+
+function SetTimePage({translate, privatePersonalDetails, customStatus}) {
+ usePrivatePersonalDetails();
+
+ const styles = useThemeStyles();
+ const clearAfter = lodashGet(customStatus, 'clearAfter', '');
+
+ const onSubmit = (time) => {
+ const timeToUse = DateUtils.combineDateAndTime(time, clearAfter);
+
+ User.updateDraftCustomStatus({clearAfter: timeToUse});
+ Navigation.goBack(ROUTES.SETTINGS_STATUS_CLEAR_AFTER);
+ };
+
+ if (lodashGet(privatePersonalDetails, 'isLoading', true)) {
+ return ;
+ }
+ return (
+
+ Navigation.goBack(ROUTES.SETTINGS_STATUS_CLEAR_AFTER)}
+ />
+
+
+
+
+ );
+}
+
+SetTimePage.propTypes = propTypes;
+SetTimePage.displayName = 'SetTimePage';
+
+export default compose(
+ withLocalize,
+ withOnyx({
+ privatePersonalDetails: {
+ key: ONYXKEYS.PRIVATE_PERSONAL_DETAILS,
+ },
+ customStatus: {
+ key: ONYXKEYS.CUSTOM_STATUS_DRAFT,
+ },
+ }),
+)(SetTimePage);
diff --git a/src/pages/settings/Profile/CustomStatus/StatusClearAfterPage.js b/src/pages/settings/Profile/CustomStatus/StatusClearAfterPage.js
new file mode 100644
index 000000000000..569435048383
--- /dev/null
+++ b/src/pages/settings/Profile/CustomStatus/StatusClearAfterPage.js
@@ -0,0 +1,247 @@
+import _ from 'lodash';
+import lodashGet from 'lodash/get';
+import PropTypes from 'prop-types';
+import React, {useCallback, useEffect, useMemo, useState} from 'react';
+import {View} from 'react-native';
+import {withOnyx} from 'react-native-onyx';
+import FormProvider from '@components/Form/FormProvider';
+import HeaderWithBackButton from '@components/HeaderWithBackButton';
+import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
+import ScreenWrapper from '@components/ScreenWrapper';
+import BaseListItem from '@components/SelectionList/BaseListItem';
+import Text from '@components/Text';
+import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps} from '@components/withCurrentUserPersonalDetails';
+import withLocalize from '@components/withLocalize';
+import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
+import * as User from '@libs/actions/User';
+import compose from '@libs/compose';
+import DateUtils from '@libs/DateUtils';
+import Navigation from '@libs/Navigation/Navigation';
+import * as ValidationUtils from '@libs/ValidationUtils';
+import personalDetailsPropType from '@pages/personalDetailsPropType';
+import CONST from '@src/CONST';
+import ONYXKEYS from '@src/ONYXKEYS';
+import ROUTES from '@src/ROUTES';
+
+const defaultProps = {
+ ...withCurrentUserPersonalDetailsDefaultProps,
+};
+
+const propTypes = {
+ currentUserPersonalDetails: personalDetailsPropType,
+ customStatus: PropTypes.shape({
+ clearAfter: PropTypes.string,
+ }),
+};
+
+/**
+ * @param {string} data - either a value from CONST.CUSTOM_STATUS_TYPES or a dateTime string in the format YYYY-MM-DD HH:mm
+ * @returns {string}
+ */
+function getSelectedStatusType(data) {
+ switch (data) {
+ case DateUtils.getEndOfToday():
+ return CONST.CUSTOM_STATUS_TYPES.AFTER_TODAY;
+ case CONST.CUSTOM_STATUS_TYPES.NEVER:
+ case '':
+ return CONST.CUSTOM_STATUS_TYPES.NEVER;
+ case false:
+ return CONST.CUSTOM_STATUS_TYPES.AFTER_TODAY;
+ default:
+ return CONST.CUSTOM_STATUS_TYPES.CUSTOM;
+ }
+}
+
+const useValidateCustomDate = (data) => {
+ const {translate} = useLocalize();
+ const [customDateError, setCustomDateError] = useState('');
+ const [customTimeError, setCustomTimeError] = useState('');
+ const validate = () => {
+ const {dateValidationErrorKey, timeValidationErrorKey} = ValidationUtils.validateDateTimeIsAtLeastOneMinuteInFuture(data);
+
+ const dateError = dateValidationErrorKey ? translate(dateValidationErrorKey) : '';
+ setCustomDateError(dateError);
+
+ const timeError = timeValidationErrorKey ? translate(timeValidationErrorKey) : '';
+ setCustomTimeError(timeError);
+
+ return {
+ dateError,
+ timeError,
+ };
+ };
+
+ useEffect(() => {
+ if (!data) {
+ return;
+ }
+ validate();
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [data]);
+
+ const validateCustomDate = () => validate();
+
+ return {customDateError, customTimeError, validateCustomDate};
+};
+
+function StatusClearAfterPage({currentUserPersonalDetails, customStatus}) {
+ const styles = useThemeStyles();
+ const {translate} = useLocalize();
+ const clearAfter = lodashGet(currentUserPersonalDetails, 'status.clearAfter', '');
+ const draftClearAfter = lodashGet(customStatus, 'clearAfter', '');
+ const [draftPeriod, setDraftPeriod] = useState(getSelectedStatusType(draftClearAfter || clearAfter));
+ const statusType = useMemo(
+ () =>
+ _.map(CONST.CUSTOM_STATUS_TYPES, (value, key) => ({
+ value,
+ text: translate(`statusPage.timePeriods.${value}`),
+ keyForList: key,
+ isSelected: draftPeriod === value,
+ })),
+ [draftPeriod, translate],
+ );
+
+ const {customDateError, customTimeError, validateCustomDate} = useValidateCustomDate(draftClearAfter);
+
+ const {redBrickDateIndicator, redBrickTimeIndicator} = useMemo(
+ () => ({
+ redBrickDateIndicator: customDateError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : null,
+ redBrickTimeIndicator: customTimeError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : null,
+ }),
+ [customTimeError, customDateError],
+ );
+
+ const onSubmit = () => {
+ const {dateError, timeError} = validateCustomDate();
+ if (dateError || timeError) {
+ return;
+ }
+ let calculatedDraftDate = '';
+ if (draftPeriod === CONST.CUSTOM_STATUS_TYPES.CUSTOM) {
+ calculatedDraftDate = draftClearAfter;
+ } else {
+ const selectedRange = _.find(statusType, (item) => item.isSelected);
+ calculatedDraftDate = DateUtils.getDateFromStatusType(selectedRange.value);
+ }
+ User.updateDraftCustomStatus({clearAfter: calculatedDraftDate});
+ Navigation.goBack(ROUTES.SETTINGS_STATUS);
+ };
+
+ const updateMode = useCallback(
+ (mode) => {
+ if (mode.value === draftPeriod) {
+ return;
+ }
+ setDraftPeriod(mode.value);
+
+ if (mode.value === CONST.CUSTOM_STATUS_TYPES.CUSTOM) {
+ User.updateDraftCustomStatus({clearAfter: DateUtils.getOneHourFromNow()});
+ } else {
+ const selectedRange = _.find(statusType, (item) => item.value === mode.value);
+ const calculatedDraftDate = DateUtils.getDateFromStatusType(selectedRange.value);
+ User.updateDraftCustomStatus({clearAfter: calculatedDraftDate});
+ Navigation.goBack(ROUTES.SETTINGS_STATUS);
+ }
+ },
+ [draftPeriod, statusType],
+ );
+
+ useEffect(() => {
+ User.updateDraftCustomStatus({
+ clearAfter: draftClearAfter || clearAfter,
+ });
+
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ const customStatusDate = DateUtils.extractDate(draftClearAfter);
+ const customStatusTime = DateUtils.extractTime12Hour(draftClearAfter);
+
+ const timePeriodOptions = useCallback(
+ () =>
+ _.map(statusType, (item, index) => (
+ updateMode(item)}
+ showTooltip={false}
+ />
+ )),
+ [statusType, updateMode],
+ );
+
+ return (
+
+ Navigation.goBack(ROUTES.SETTINGS_STATUS)}
+ />
+ {translate('statusPage.whenClearStatus')}
+
+
+ {timePeriodOptions()}
+ {draftPeriod === CONST.CUSTOM_STATUS_TYPES.CUSTOM && (
+ <>
+ Navigation.navigate(ROUTES.SETTINGS_STATUS_CLEAR_AFTER_DATE)}
+ errorText={customDateError}
+ titleTextStyle={styles.flex1}
+ brickRoadIndicator={redBrickDateIndicator}
+ />
+ Navigation.navigate(ROUTES.SETTINGS_STATUS_CLEAR_AFTER_TIME)}
+ errorText={customTimeError}
+ titleTextStyle={styles.flex1}
+ brickRoadIndicator={redBrickTimeIndicator}
+ />
+ >
+ )}
+
+
+
+ );
+}
+
+StatusClearAfterPage.displayName = 'StatusClearAfterPage';
+StatusClearAfterPage.propTypes = propTypes;
+StatusClearAfterPage.defaultProps = defaultProps;
+
+export default compose(
+ withCurrentUserPersonalDetails,
+ withLocalize,
+ withOnyx({
+ timePeriodType: {
+ key: `${ONYXKEYS.FORMS.SETTINGS_STATUS_SET_CLEAR_AFTER_FORM}Draft`,
+ },
+ clearDateForm: {
+ key: `${ONYXKEYS.FORMS.SETTINGS_STATUS_CLEAR_DATE_FORM}Draft`,
+ },
+ customStatus: {
+ key: ONYXKEYS.CUSTOM_STATUS_DRAFT,
+ },
+ preferredLocale: {
+ key: ONYXKEYS.NVP_PREFERRED_LOCALE,
+ },
+ }),
+)(StatusClearAfterPage);
diff --git a/src/pages/settings/Profile/CustomStatus/StatusPage.js b/src/pages/settings/Profile/CustomStatus/StatusPage.js
index 226d0fed8044..bf21d3cd2b54 100644
--- a/src/pages/settings/Profile/CustomStatus/StatusPage.js
+++ b/src/pages/settings/Profile/CustomStatus/StatusPage.js
@@ -1,122 +1,195 @@
import lodashGet from 'lodash/get';
-import React, {useCallback, useEffect, useMemo} from 'react';
-import {View} from 'react-native';
+import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
+import {InteractionManager, View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
-import MobileBackgroundImage from '@assets/images/money-stack.svg';
-import Button from '@components/Button';
+import EmojiPickerButtonDropdown from '@components/EmojiPicker/EmojiPickerButtonDropdown';
+import FormProvider from '@components/Form/FormProvider';
+import InputWrapper from '@components/Form/InputWrapper';
import HeaderPageLayout from '@components/HeaderPageLayout';
+import HeaderWithBackButton from '@components/HeaderWithBackButton';
import * as Expensicons from '@components/Icon/Expensicons';
import MenuItem from '@components/MenuItem';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
+import ScreenWrapper from '@components/ScreenWrapper';
import Text from '@components/Text';
+import TextInput from '@components/TextInput';
import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails';
import useLocalize from '@hooks/useLocalize';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
+import DateUtils from '@libs/DateUtils';
import Navigation from '@libs/Navigation/Navigation';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import * as User from '@userActions/User';
+import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import SCREENS from '@src/SCREENS';
+const INPUT_IDS = {
+ EMOJI_CODE: 'emojiCode',
+ STATUS_TEXT: 'statusText',
+};
+
const propTypes = {
...withCurrentUserPersonalDetailsPropTypes,
};
+const initialEmoji = 'đź’¬';
+
function StatusPage({draftStatus, currentUserPersonalDetails}) {
const theme = useTheme();
const styles = useThemeStyles();
+ const StyleUtils = useStyleUtils();
const {translate} = useLocalize();
+ const formRef = useRef(null);
+ const [brickRoadIndicator, setBrickRoadIndicator] = useState('');
const currentUserEmojiCode = lodashGet(currentUserPersonalDetails, 'status.emojiCode', '');
const currentUserStatusText = lodashGet(currentUserPersonalDetails, 'status.text', '');
+ const currentUserClearAfter = lodashGet(currentUserPersonalDetails, 'status.clearAfter', '');
const draftEmojiCode = lodashGet(draftStatus, 'emojiCode');
const draftText = lodashGet(draftStatus, 'text');
+ const draftClearAfter = lodashGet(draftStatus, 'clearAfter');
- const defaultEmoji = draftEmojiCode || currentUserEmojiCode;
- const defaultText = draftEmojiCode ? draftText : currentUserStatusText;
- const hasDraftStatus = !!draftEmojiCode || !!draftText;
- const customStatus = useMemo(() => {
- if (draftEmojiCode) {
- return `${draftEmojiCode} ${draftText}`;
- }
- if (currentUserEmojiCode || currentUserStatusText) {
- return `${currentUserEmojiCode || ''} ${currentUserStatusText || ''}`;
+ const defaultEmoji = draftEmojiCode || currentUserEmojiCode || initialEmoji;
+ const defaultText = draftText || currentUserStatusText;
+
+ const customClearAfter = useMemo(() => {
+ const dataToShow = draftClearAfter || currentUserClearAfter;
+ return DateUtils.getLocalizedTimePeriodDescription(dataToShow);
+ }, [draftClearAfter, currentUserClearAfter]);
+
+ const isValidClearAfterDate = useCallback(() => {
+ const clearAfterTime = draftClearAfter || currentUserClearAfter;
+ if (clearAfterTime === CONST.CUSTOM_STATUS_TYPES.NEVER || clearAfterTime === '') {
+ return true;
}
- return '';
- }, [draftEmojiCode, draftText, currentUserEmojiCode, currentUserStatusText]);
+
+ return DateUtils.isTimeAtLeastOneMinuteInFuture({dateTimeString: clearAfterTime});
+ }, [draftClearAfter, currentUserClearAfter]);
+
+ const navigateBackToPreviousScreen = useCallback(() => Navigation.goBack(ROUTES.SETTINGS_PROFILE, false, true), []);
+ const updateStatus = useCallback(
+ ({emojiCode, statusText}) => {
+ const clearAfterTime = draftClearAfter || currentUserClearAfter;
+ const isValid = DateUtils.isTimeAtLeastOneMinuteInFuture({dateTimeString: clearAfterTime});
+ if (!isValid && clearAfterTime !== CONST.CUSTOM_STATUS_TYPES.NEVER) {
+ setBrickRoadIndicator(isValidClearAfterDate() ? null : CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR);
+ return;
+ }
+
+ User.updateCustomStatus({
+ text: statusText,
+ emojiCode,
+ clearAfter: clearAfterTime !== CONST.CUSTOM_STATUS_TYPES.NEVER ? clearAfterTime : '',
+ });
+
+ User.clearDraftCustomStatus();
+ InteractionManager.runAfterInteractions(() => {
+ navigateBackToPreviousScreen();
+ });
+ },
+ [currentUserClearAfter, draftClearAfter, isValidClearAfterDate, navigateBackToPreviousScreen],
+ );
const clearStatus = () => {
User.clearCustomStatus();
- User.clearDraftCustomStatus();
+ User.updateDraftCustomStatus({
+ text: '',
+ emojiCode: '',
+ clearAfter: DateUtils.getEndOfToday(),
+ });
+ formRef.current.resetForm({[INPUT_IDS.EMOJI_CODE]: initialEmoji});
};
- const navigateBackToSettingsPage = useCallback(() => {
- const topMostReportID = Navigation.getTopmostReportId();
- if (topMostReportID) {
- Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(topMostReportID));
+ useEffect(() => setBrickRoadIndicator(isValidClearAfterDate() ? null : CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR), [isValidClearAfterDate]);
+
+ useEffect(() => {
+ if (!currentUserEmojiCode && !currentUserClearAfter && !draftClearAfter) {
+ User.updateDraftCustomStatus({clearAfter: DateUtils.getEndOfToday()});
} else {
- Navigation.goBack(ROUTES.SETTINGS_PROFILE, false, true);
+ User.updateDraftCustomStatus({clearAfter: currentUserClearAfter});
}
- }, []);
- const updateStatus = useCallback(() => {
- User.updateCustomStatus({text: defaultText, emojiCode: defaultEmoji});
-
- User.clearDraftCustomStatus();
- Navigation.goBack(ROUTES.SETTINGS_PROFILE);
- }, [defaultText, defaultEmoji]);
- const footerComponent = useMemo(
- () =>
- hasDraftStatus ? (
-
- ) : null,
- [hasDraftStatus, translate, updateStatus],
- );
+ return () => User.clearDraftCustomStatus();
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
- useEffect(() => () => User.clearDraftCustomStatus(), []);
+ const validateForm = useCallback(() => {
+ if (brickRoadIndicator) {
+ return {clearAfter: ''};
+ }
+ return {};
+ }, [brickRoadIndicator]);
return (
-
- }
- headerContainerStyles={[styles.staticHeaderImage]}
- backgroundColor={theme.PAGE_THEMES[SCREENS.SETTINGS.PROFILE.STATUS].backgroundColor}
- footer={footerComponent}
+
-
- {translate('statusPage.setStatusTitle')}
- {translate('statusPage.statusExplanation')}
-
- Navigation.navigate(ROUTES.SETTINGS_STATUS_SET)}
+
-
- {(!!currentUserEmojiCode || !!currentUserStatusText) && (
-
- )}
-
+
+
+ {translate('statusPage.statusExplanation')}
+
+
+
+
+
+
+ Navigation.navigate(ROUTES.SETTINGS_STATUS_CLEAR_AFTER)}
+ containerStyle={styles.pr2}
+ brickRoadIndicator={brickRoadIndicator}
+ />
+ {(!!currentUserEmojiCode || !!currentUserStatusText) && (
+
+ )}
+
+
+
);
}
diff --git a/src/pages/settings/Profile/CustomStatus/StatusSetPage.js b/src/pages/settings/Profile/CustomStatus/StatusSetPage.js
deleted file mode 100644
index b9ae9c8d2f51..000000000000
--- a/src/pages/settings/Profile/CustomStatus/StatusSetPage.js
+++ /dev/null
@@ -1,102 +0,0 @@
-import lodashGet from 'lodash/get';
-import PropTypes from 'prop-types';
-import React from 'react';
-import {View} from 'react-native';
-import {withOnyx} from 'react-native-onyx';
-import EmojiPickerButtonDropdown from '@components/EmojiPicker/EmojiPickerButtonDropdown';
-import FormProvider from '@components/Form/FormProvider';
-import InputWrapper from '@components/Form/InputWrapper';
-import HeaderWithBackButton from '@components/HeaderWithBackButton';
-import ScreenWrapper from '@components/ScreenWrapper';
-import TextInput from '@components/TextInput';
-import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails';
-import useLocalize from '@hooks/useLocalize';
-import compose from '@libs/compose';
-import Navigation from '@libs/Navigation/Navigation';
-import useThemeStyles from '@styles/useThemeStyles';
-import * as User from '@userActions/User';
-import CONST from '@src/CONST';
-import ONYXKEYS from '@src/ONYXKEYS';
-import ROUTES from '@src/ROUTES';
-
-const propTypes = {
- /** The draft status of the user */
- // eslint-disable-next-line react/require-default-props
- draftStatus: PropTypes.shape({
- /** The emoji code of the draft status */
- emojiCode: PropTypes.string,
- /** The text of the draft status */
- text: PropTypes.string,
- }),
-
- ...withCurrentUserPersonalDetailsPropTypes,
-};
-
-const INPUT_IDS = {
- EMOJI_CODE: 'emojiCode',
- STATUS_TEXT: 'statusText',
-};
-
-function StatusSetPage({draftStatus, currentUserPersonalDetails}) {
- const styles = useThemeStyles();
- const {translate} = useLocalize();
- const defaultEmoji = lodashGet(draftStatus, 'emojiCode') || lodashGet(currentUserPersonalDetails, 'status.emojiCode', 'đź’¬');
- const defaultText = lodashGet(draftStatus, 'text') || lodashGet(currentUserPersonalDetails, 'status.text', '');
-
- const onSubmit = (value) => {
- User.updateDraftCustomStatus({text: value.statusText.trim(), emojiCode: value.emojiCode});
- Navigation.goBack(ROUTES.SETTINGS_STATUS);
- };
-
- return (
-
- Navigation.goBack(ROUTES.SETTINGS_STATUS)}
- />
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-StatusSetPage.displayName = 'StatusSetPage';
-StatusSetPage.propTypes = propTypes;
-
-export default compose(
- withCurrentUserPersonalDetails,
- withOnyx({
- draftStatus: {
- key: ONYXKEYS.CUSTOM_STATUS_DRAFT,
- },
- }),
-)(StatusSetPage);
diff --git a/src/pages/settings/Profile/DisplayNamePage.js b/src/pages/settings/Profile/DisplayNamePage.js
index 5c13bfe72d5f..8ea471283004 100644
--- a/src/pages/settings/Profile/DisplayNamePage.js
+++ b/src/pages/settings/Profile/DisplayNamePage.js
@@ -12,11 +12,11 @@ import Text from '@components/Text';
import TextInput from '@components/TextInput';
import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import * as ErrorUtils from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as ValidationUtils from '@libs/ValidationUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import * as PersonalDetails from '@userActions/PersonalDetails';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/settings/Profile/LoungeAccessPage.js b/src/pages/settings/Profile/LoungeAccessPage.js
index f6b6968372d1..60cb0896a4eb 100644
--- a/src/pages/settings/Profile/LoungeAccessPage.js
+++ b/src/pages/settings/Profile/LoungeAccessPage.js
@@ -5,11 +5,11 @@ import LottieAnimations from '@components/LottieAnimations';
import Text from '@components/Text';
import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
import NotFoundPage from '@pages/ErrorPage/NotFoundPage';
import userPropTypes from '@pages/settings/userPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.js b/src/pages/settings/Profile/PersonalDetails/AddressPage.js
index fa22a3b22f9e..6c883e7fa9d8 100644
--- a/src/pages/settings/Profile/PersonalDetails/AddressPage.js
+++ b/src/pages/settings/Profile/PersonalDetails/AddressPage.js
@@ -8,8 +8,8 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import useLocalize from '@hooks/useLocalize';
import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails';
+import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
-import useThemeStyles from '@styles/useThemeStyles';
import * as PersonalDetails from '@userActions/PersonalDetails';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
diff --git a/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js b/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js
index 10a6d8807afc..393e95cb35fe 100644
--- a/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js
+++ b/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js
@@ -10,10 +10,10 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
import * as ValidationUtils from '@libs/ValidationUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import * as PersonalDetails from '@userActions/PersonalDetails';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js b/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js
index a9005c39534d..365ea62184ab 100644
--- a/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js
+++ b/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js
@@ -12,11 +12,11 @@ import ScreenWrapper from '@components/ScreenWrapper';
import TextInput from '@components/TextInput';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import * as ErrorUtils from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as ValidationUtils from '@libs/ValidationUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import * as PersonalDetails from '@userActions/PersonalDetails';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/settings/Profile/PersonalDetails/PersonalDetailsInitialPage.js b/src/pages/settings/Profile/PersonalDetails/PersonalDetailsInitialPage.js
index 4fc2bbb17428..cf6887b7e04c 100644
--- a/src/pages/settings/Profile/PersonalDetails/PersonalDetailsInitialPage.js
+++ b/src/pages/settings/Profile/PersonalDetails/PersonalDetailsInitialPage.js
@@ -11,10 +11,10 @@ import ScreenWrapper from '@components/ScreenWrapper';
import Text from '@components/Text';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js
index fc6c38f96335..89dfa4f0e419 100755
--- a/src/pages/settings/Profile/ProfilePage.js
+++ b/src/pages/settings/Profile/ProfilePage.js
@@ -13,11 +13,11 @@ import ScreenWrapper from '@components/ScreenWrapper';
import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
import * as UserUtils from '@libs/UserUtils';
import userPropTypes from '@pages/settings/userPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as App from '@userActions/App';
import * as PersonalDetails from '@userActions/PersonalDetails';
import CONST from '@src/CONST';
diff --git a/src/pages/settings/Profile/PronounsPage.js b/src/pages/settings/Profile/PronounsPage.js
index 40f2562724ef..20022f22523a 100644
--- a/src/pages/settings/Profile/PronounsPage.js
+++ b/src/pages/settings/Profile/PronounsPage.js
@@ -10,9 +10,9 @@ import SelectionList from '@components/SelectionList';
import Text from '@components/Text';
import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
-import useThemeStyles from '@styles/useThemeStyles';
import * as PersonalDetails from '@userActions/PersonalDetails';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/settings/Profile/TimezoneInitialPage.js b/src/pages/settings/Profile/TimezoneInitialPage.js
index 463b20029745..bf86e8a5a077 100644
--- a/src/pages/settings/Profile/TimezoneInitialPage.js
+++ b/src/pages/settings/Profile/TimezoneInitialPage.js
@@ -8,9 +8,9 @@ import Switch from '@components/Switch';
import Text from '@components/Text';
import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
-import useThemeStyles from '@styles/useThemeStyles';
import * as PersonalDetails from '@userActions/PersonalDetails';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
diff --git a/src/pages/settings/Report/ReportSettingsPage.js b/src/pages/settings/Report/ReportSettingsPage.js
index 0676f0875932..c7cfd9c7850d 100644
--- a/src/pages/settings/Report/ReportSettingsPage.js
+++ b/src/pages/settings/Report/ReportSettingsPage.js
@@ -14,13 +14,13 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback';
import ScreenWrapper from '@components/ScreenWrapper';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import {getGroupChatName} from '@libs/GroupChatUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as ReportUtils from '@libs/ReportUtils';
import withReportOrNotFound from '@pages/home/report/withReportOrNotFound';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Report from '@userActions/Report';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/settings/Report/RoomNamePage.js b/src/pages/settings/Report/RoomNamePage.js
index 7916043c7e20..5f64faca50fc 100644
--- a/src/pages/settings/Report/RoomNamePage.js
+++ b/src/pages/settings/Report/RoomNamePage.js
@@ -4,11 +4,13 @@ import React, {useCallback, useRef} from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
-import Form from '@components/Form';
+import FormProvider from '@components/Form/FormProvider';
+import InputWrapper from '@components/Form/InputWrapper';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import RoomNameInput from '@components/RoomNameInput';
import ScreenWrapper from '@components/ScreenWrapper';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import * as ErrorUtils from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
@@ -16,7 +18,6 @@ import * as ReportUtils from '@libs/ReportUtils';
import * as ValidationUtils from '@libs/ValidationUtils';
import withReportOrNotFound from '@pages/home/report/withReportOrNotFound';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Report from '@userActions/Report';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
@@ -42,13 +43,8 @@ const defaultProps = {
policy: {},
};
-function RoomNamePage(props) {
+function RoomNamePage({policy, report, reports, translate}) {
const styles = useThemeStyles();
- const policy = props.policy;
- const report = props.report;
- const reports = props.reports;
- const translate = props.translate;
-
const roomNameInputRef = useRef(null);
const isFocused = useIsFocused();
@@ -91,7 +87,7 @@ function RoomNamePage(props) {
title={translate('newRoomPage.roomName')}
onBackButtonPress={() => Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(report.reportID))}
/>
-
+
);
diff --git a/src/pages/settings/Security/CloseAccountPage.js b/src/pages/settings/Security/CloseAccountPage.js
index 2c55c5e73f0c..9aad345d7b3d 100644
--- a/src/pages/settings/Security/CloseAccountPage.js
+++ b/src/pages/settings/Security/CloseAccountPage.js
@@ -12,10 +12,10 @@ import Text from '@components/Text';
import TextInput from '@components/TextInput';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
import * as ValidationUtils from '@libs/ValidationUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import * as CloseAccount from '@userActions/CloseAccount';
import * as User from '@userActions/User';
import CONST from '@src/CONST';
diff --git a/src/pages/settings/Security/SecuritySettingsPage.js b/src/pages/settings/Security/SecuritySettingsPage.js
index 95a0efa2fc39..392a264977c6 100644
--- a/src/pages/settings/Security/SecuritySettingsPage.js
+++ b/src/pages/settings/Security/SecuritySettingsPage.js
@@ -8,11 +8,11 @@ import IllustratedHeaderPageLayout from '@components/IllustratedHeaderPageLayout
import LottieAnimations from '@components/LottieAnimations';
import MenuItemList from '@components/MenuItemList';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWaitForNavigation from '@hooks/useWaitForNavigation';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import SCREENS from '@src/SCREENS';
diff --git a/src/pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper.js b/src/pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper.js
index 69d662ba6e81..ba899a0e2d20 100644
--- a/src/pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper.js
+++ b/src/pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper.js
@@ -4,7 +4,7 @@ import useAnimatedStepContext from '@components/AnimatedStep/useAnimatedStepCont
import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as TwoFactorAuthActions from '@userActions/TwoFactorAuthActions';
import StepWrapperPropTypes from './StepWrapperPropTypes';
diff --git a/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.js b/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.js
index 86d218ec63ae..420d976dcd26 100644
--- a/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.js
+++ b/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.js
@@ -11,14 +11,14 @@ import PressableWithDelayToggle from '@components/Pressable/PressableWithDelayTo
import Section from '@components/Section';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import Clipboard from '@libs/Clipboard';
import localFileDownload from '@libs/localFileDownload';
import StepWrapper from '@pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper';
import useTwoFactorAuthContext from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthContext/useTwoFactorAuth';
import {defaultAccount, TwoFactorAuthPropTypes} from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthPropTypes';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Session from '@userActions/Session';
import * as TwoFactorAuthActions from '@userActions/TwoFactorAuthActions';
import CONST from '@src/CONST';
diff --git a/src/pages/settings/Security/TwoFactorAuth/Steps/DisabledStep.js b/src/pages/settings/Security/TwoFactorAuth/Steps/DisabledStep.js
index 9de25ecb8f4b..db64a805d088 100644
--- a/src/pages/settings/Security/TwoFactorAuth/Steps/DisabledStep.js
+++ b/src/pages/settings/Security/TwoFactorAuth/Steps/DisabledStep.js
@@ -4,8 +4,8 @@ import Button from '@components/Button';
import FixedFooter from '@components/FixedFooter';
import * as Illustrations from '@components/Icon/Illustrations';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import StepWrapper from '@pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import * as TwoFactorAuthActions from '@userActions/TwoFactorAuthActions';
diff --git a/src/pages/settings/Security/TwoFactorAuth/Steps/EnabledStep.js b/src/pages/settings/Security/TwoFactorAuth/Steps/EnabledStep.js
index e0f90c57f350..27f9c0f04404 100644
--- a/src/pages/settings/Security/TwoFactorAuth/Steps/EnabledStep.js
+++ b/src/pages/settings/Security/TwoFactorAuth/Steps/EnabledStep.js
@@ -5,10 +5,10 @@ import * as Expensicons from '@components/Icon/Expensicons';
import * as Illustrations from '@components/Icon/Illustrations';
import Section from '@components/Section';
import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import StepWrapper from '@pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper';
import useTwoFactorAuthContext from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthContext/useTwoFactorAuth';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Session from '@userActions/Session';
import CONST from '@src/CONST';
diff --git a/src/pages/settings/Security/TwoFactorAuth/Steps/VerifyStep.js b/src/pages/settings/Security/TwoFactorAuth/Steps/VerifyStep.js
index 8a2b4c6fdf6c..e5f809204bd6 100644
--- a/src/pages/settings/Security/TwoFactorAuth/Steps/VerifyStep.js
+++ b/src/pages/settings/Security/TwoFactorAuth/Steps/VerifyStep.js
@@ -11,12 +11,12 @@ import QRCode from '@components/QRCode';
import Text from '@components/Text';
import TextLink from '@components/TextLink';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import Clipboard from '@libs/Clipboard';
import StepWrapper from '@pages/settings/Security/TwoFactorAuth/StepWrapper/StepWrapper';
import useTwoFactorAuthContext from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthContext/useTwoFactorAuth';
import TwoFactorAuthForm from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm';
import {defaultAccount, TwoFactorAuthPropTypes} from '@pages/settings/Security/TwoFactorAuth/TwoFactorAuthPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Session from '@userActions/Session';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/settings/Wallet/ActivatePhysicalCardPage.js b/src/pages/settings/Wallet/ActivatePhysicalCardPage.js
index 788c571d9e7d..bfc8583998b6 100644
--- a/src/pages/settings/Wallet/ActivatePhysicalCardPage.js
+++ b/src/pages/settings/Wallet/ActivatePhysicalCardPage.js
@@ -12,14 +12,14 @@ import MagicCodeInput from '@components/MagicCodeInput';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as CardUtils from '@libs/CardUtils';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import * as ErrorUtils from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
import NotFoundPage from '@pages/ErrorPage/NotFoundPage';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import * as CardSettings from '@userActions/Card';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/settings/Wallet/AddDebitCardPage.js b/src/pages/settings/Wallet/AddDebitCardPage.js
index 1f824fed5293..0704bf6bf3b8 100644
--- a/src/pages/settings/Wallet/AddDebitCardPage.js
+++ b/src/pages/settings/Wallet/AddDebitCardPage.js
@@ -14,9 +14,9 @@ import TextInput from '@components/TextInput';
import TextLink from '@components/TextLink';
import useLocalize from '@hooks/useLocalize';
import usePrevious from '@hooks/usePrevious';
+import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import * as ValidationUtils from '@libs/ValidationUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import * as PaymentMethods from '@userActions/PaymentMethods';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.js b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.js
index 4176393e92dd..1d1ce906189b 100644
--- a/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.js
+++ b/src/pages/settings/Wallet/Card/BaseGetPhysicalCard.js
@@ -6,6 +6,7 @@ import _ from 'underscore';
import Form from '@components/Form';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as FormActions from '@libs/actions/FormActions';
import * as Wallet from '@libs/actions/Wallet';
import * as CardUtils from '@libs/CardUtils';
@@ -13,7 +14,6 @@ import FormUtils from '@libs/FormUtils';
import * as GetPhysicalCardUtils from '@libs/GetPhysicalCardUtils';
import Navigation from '@libs/Navigation/Navigation';
import assignedCardPropTypes from '@pages/settings/Wallet/assignedCardPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
diff --git a/src/pages/settings/Wallet/Card/GetPhysicalCardConfirm.js b/src/pages/settings/Wallet/Card/GetPhysicalCardConfirm.js
index 0ae83cd28b5c..9f364c32c075 100644
--- a/src/pages/settings/Wallet/Card/GetPhysicalCardConfirm.js
+++ b/src/pages/settings/Wallet/Card/GetPhysicalCardConfirm.js
@@ -5,10 +5,10 @@ import * as Expensicons from '@components/Icon/Expensicons';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import FormUtils from '@libs/FormUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
diff --git a/src/pages/settings/Wallet/Card/GetPhysicalCardName.js b/src/pages/settings/Wallet/Card/GetPhysicalCardName.js
index 3c87bed424ba..0040dac8b75f 100644
--- a/src/pages/settings/Wallet/Card/GetPhysicalCardName.js
+++ b/src/pages/settings/Wallet/Card/GetPhysicalCardName.js
@@ -4,9 +4,9 @@ import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import TextInput from '@components/TextInput';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import FormUtils from '@libs/FormUtils';
import * as ValidationUtils from '@libs/ValidationUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
diff --git a/src/pages/settings/Wallet/Card/GetPhysicalCardPhone.js b/src/pages/settings/Wallet/Card/GetPhysicalCardPhone.js
index 82b684332491..3d4c7f4ac6fb 100644
--- a/src/pages/settings/Wallet/Card/GetPhysicalCardPhone.js
+++ b/src/pages/settings/Wallet/Card/GetPhysicalCardPhone.js
@@ -6,8 +6,8 @@ import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import TextInput from '@components/TextInput';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import FormUtils from '@libs/FormUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
diff --git a/src/pages/settings/Wallet/ChooseTransferAccountPage.js b/src/pages/settings/Wallet/ChooseTransferAccountPage.js
index 2ffbf12f52c2..815d2e883cf0 100644
--- a/src/pages/settings/Wallet/ChooseTransferAccountPage.js
+++ b/src/pages/settings/Wallet/ChooseTransferAccountPage.js
@@ -6,9 +6,9 @@ import * as Expensicons from '@components/Icon/Expensicons';
import MenuItem from '@components/MenuItem';
import ScreenWrapper from '@components/ScreenWrapper';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
-import useThemeStyles from '@styles/useThemeStyles';
import * as BankAccounts from '@userActions/BankAccounts';
import * as PaymentMethods from '@userActions/PaymentMethods';
import CONST from '@src/CONST';
diff --git a/src/pages/settings/Wallet/ExpensifyCardPage.js b/src/pages/settings/Wallet/ExpensifyCardPage.js
index b469027e6a57..513da42b9011 100644
--- a/src/pages/settings/Wallet/ExpensifyCardPage.js
+++ b/src/pages/settings/Wallet/ExpensifyCardPage.js
@@ -13,13 +13,13 @@ import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import ScreenWrapper from '@components/ScreenWrapper';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as CardUtils from '@libs/CardUtils';
import * as CurrencyUtils from '@libs/CurrencyUtils';
import FormUtils from '@libs/FormUtils';
import * as GetPhysicalCardUtils from '@libs/GetPhysicalCardUtils';
import Navigation from '@libs/Navigation/Navigation';
import NotFoundPage from '@pages/ErrorPage/NotFoundPage';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Card from '@userActions/Card';
import * as Link from '@userActions/Link';
import CONST from '@src/CONST';
diff --git a/src/pages/settings/Wallet/PaymentMethodList.js b/src/pages/settings/Wallet/PaymentMethodList.js
index 4f6d0331f3b4..6e424a918b7e 100644
--- a/src/pages/settings/Wallet/PaymentMethodList.js
+++ b/src/pages/settings/Wallet/PaymentMethodList.js
@@ -16,13 +16,13 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as CardUtils from '@libs/CardUtils';
import Log from '@libs/Log';
import Navigation from '@libs/Navigation/Navigation';
import * as PaymentUtils from '@libs/PaymentUtils';
import stylePropTypes from '@styles/stylePropTypes';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import * as PaymentMethods from '@userActions/PaymentMethods';
import CONST from '@src/CONST';
diff --git a/src/pages/settings/Wallet/RedDotCardSection.js b/src/pages/settings/Wallet/RedDotCardSection.js
index 65c700575340..7025cf7bf1a2 100644
--- a/src/pages/settings/Wallet/RedDotCardSection.js
+++ b/src/pages/settings/Wallet/RedDotCardSection.js
@@ -4,8 +4,8 @@ import {View} from 'react-native';
import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import Text from '@components/Text';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
const propTypes = {
title: PropTypes.string.isRequired,
diff --git a/src/pages/settings/Wallet/ReportCardLostPage.js b/src/pages/settings/Wallet/ReportCardLostPage.js
index aa313b73a6bd..b01dc99cb485 100644
--- a/src/pages/settings/Wallet/ReportCardLostPage.js
+++ b/src/pages/settings/Wallet/ReportCardLostPage.js
@@ -12,11 +12,11 @@ import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import usePrevious from '@hooks/usePrevious';
import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as CardUtils from '@libs/CardUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
import NotFoundPage from '@pages/ErrorPage/NotFoundPage';
-import useThemeStyles from '@styles/useThemeStyles';
import * as CardActions from '@userActions/Card';
import * as FormActions from '@userActions/FormActions';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/settings/Wallet/ReportVirtualCardFraudPage.js b/src/pages/settings/Wallet/ReportVirtualCardFraudPage.js
index 01d3c272ad8b..fa88e01d8b41 100644
--- a/src/pages/settings/Wallet/ReportVirtualCardFraudPage.js
+++ b/src/pages/settings/Wallet/ReportVirtualCardFraudPage.js
@@ -9,11 +9,11 @@ import ScreenWrapper from '@components/ScreenWrapper';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import usePrevious from '@hooks/usePrevious';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as CardUtils from '@libs/CardUtils';
import * as ErrorUtils from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
import NotFoundPage from '@pages/ErrorPage/NotFoundPage';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Card from '@userActions/Card';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
diff --git a/src/pages/settings/Wallet/TransferBalancePage.js b/src/pages/settings/Wallet/TransferBalancePage.js
index 2414edb29087..303baa4b4b13 100644
--- a/src/pages/settings/Wallet/TransferBalancePage.js
+++ b/src/pages/settings/Wallet/TransferBalancePage.js
@@ -15,12 +15,12 @@ import {withNetwork} from '@components/OnyxProvider';
import ScreenWrapper from '@components/ScreenWrapper';
import Text from '@components/Text';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import * as CurrencyUtils from '@libs/CurrencyUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as PaymentUtils from '@libs/PaymentUtils';
import userWalletPropTypes from '@pages/EnablePayments/userWalletPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import * as PaymentMethods from '@userActions/PaymentMethods';
import CONST from '@src/CONST';
diff --git a/src/pages/settings/Wallet/WalletEmptyState.js b/src/pages/settings/Wallet/WalletEmptyState.js
index f4609bdc8534..7a3a9e9ce6b7 100644
--- a/src/pages/settings/Wallet/WalletEmptyState.js
+++ b/src/pages/settings/Wallet/WalletEmptyState.js
@@ -6,8 +6,8 @@ import * as Illustrations from '@components/Icon/Illustrations';
import IllustratedHeaderPageLayout from '@components/IllustratedHeaderPageLayout';
import LottieAnimations from '@components/LottieAnimations';
import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
import Navigation from '@libs/Navigation/Navigation';
-import useTheme from '@styles/themes/useTheme';
import ROUTES from '@src/ROUTES';
import SCREENS from '@src/SCREENS';
diff --git a/src/pages/settings/Wallet/WalletPage/CardDetails.js b/src/pages/settings/Wallet/WalletPage/CardDetails.js
index a51c4c5fad6f..b51c34e89d17 100644
--- a/src/pages/settings/Wallet/WalletPage/CardDetails.js
+++ b/src/pages/settings/Wallet/WalletPage/CardDetails.js
@@ -8,10 +8,10 @@ import PressableWithDelayToggle from '@components/Pressable/PressableWithDelayTo
import TextLink from '@components/TextLink';
import useLocalize from '@hooks/useLocalize';
import usePrivatePersonalDetails from '@hooks/usePrivatePersonalDetails';
+import useThemeStyles from '@hooks/useThemeStyles';
import Clipboard from '@libs/Clipboard';
import Navigation from '@libs/Navigation/Navigation';
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
diff --git a/src/pages/settings/Wallet/WalletPage/WalletPage.js b/src/pages/settings/Wallet/WalletPage/WalletPage.js
index 6f452eed3629..e0577930b73d 100644
--- a/src/pages/settings/Wallet/WalletPage/WalletPage.js
+++ b/src/pages/settings/Wallet/WalletPage/WalletPage.js
@@ -1,11 +1,11 @@
import lodashGet from 'lodash/get';
import React, {useCallback, useEffect, useRef, useState} from 'react';
-import {ActivityIndicator, InteractionManager, ScrollView, View} from 'react-native';
+import {ActivityIndicator, ScrollView, View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import AddPaymentMethodMenu from '@components/AddPaymentMethodMenu';
import Button from '@components/Button';
-import ConfirmContent from '@components/ConfirmContent';
+import ConfirmModal from '@components/ConfirmModal';
import CurrentWalletBalance from '@components/CurrentWalletBalance';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import Icon from '@components/Icon';
@@ -20,6 +20,8 @@ import ScreenWrapper from '@components/ScreenWrapper';
import Text from '@components/Text';
import WalletSection from '@components/WalletSection';
import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import compose from '@libs/compose';
import getClickedTargetLocation from '@libs/getClickedTargetLocation';
@@ -27,8 +29,6 @@ import Navigation from '@libs/Navigation/Navigation';
import * as PaymentUtils from '@libs/PaymentUtils';
import PaymentMethodList from '@pages/settings/Wallet/PaymentMethodList';
import WalletEmptyState from '@pages/settings/Wallet/WalletEmptyState';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
import * as BankAccounts from '@userActions/BankAccounts';
import * as PaymentMethods from '@userActions/PaymentMethods';
@@ -62,7 +62,7 @@ function WalletPage({bankAccountList, cardList, fundList, isLoadingPaymentMethod
anchorPositionTop: 0,
anchorPositionRight: 0,
});
- const [showConfirmDeleteContent, setShowConfirmDeleteContent] = useState(false);
+ const [showConfirmDeleteModal, setShowConfirmDeleteModal] = useState(false);
const hasBankAccount = !_.isEmpty(bankAccountList) || !_.isEmpty(fundList);
const hasWallet = !_.isEmpty(userWallet);
@@ -215,10 +215,8 @@ function WalletPage({bankAccountList, cardList, fundList, isLoadingPaymentMethod
*/
const hideDefaultDeleteMenu = useCallback(() => {
setShouldShowDefaultDeleteMenu(false);
- InteractionManager.runAfterInteractions(() => {
- setShowConfirmDeleteContent(false);
- });
- }, [setShouldShowDefaultDeleteMenu, setShowConfirmDeleteContent]);
+ setShowConfirmDeleteModal(false);
+ }, [setShouldShowDefaultDeleteMenu, setShowConfirmDeleteModal]);
const makeDefaultPaymentMethod = useCallback(() => {
const paymentCardList = fundList || {};
@@ -487,11 +485,9 @@ function WalletPage({bankAccountList, cardList, fundList, isLoadingPaymentMethod
top: anchorPosition.anchorPositionTop,
right: anchorPosition.anchorPositionRight,
}}
- withoutOverlay
anchorRef={paymentMethodButtonRef}
- onModalHide={resetSelectedPaymentMethodData}
>
- {!showConfirmDeleteContent ? (
+ {!showConfirmDeleteModal && (
{isPopoverBottomMount && (
)}
{
- setShowConfirmDeleteContent(true);
- }}
+ onPress={() => setShowConfirmDeleteModal(true)}
style={[shouldShowMakeDefaultButton ? styles.mt4 : {}]}
text={translate('common.delete')}
danger
/>
- ) : (
- {
- deletePaymentMethod();
- hideDefaultDeleteMenu();
- }}
- onCancel={hideDefaultDeleteMenu}
- contentStyles={!isSmallScreenWidth ? [styles.sidebarPopover, styles.willChangeTransform] : undefined}
- title={translate('walletPage.deleteAccount')}
- prompt={translate('walletPage.deleteConfirmation')}
- confirmText={translate('common.delete')}
- cancelText={translate('common.cancel')}
- anchorPosition={{
- top: anchorPosition.anchorPositionTop,
- right: anchorPosition.anchorPositionRight,
- }}
- shouldShowCancelButton
- danger
- />
)}
+ {
+ deletePaymentMethod();
+ hideDefaultDeleteMenu();
+ }}
+ onCancel={hideDefaultDeleteMenu}
+ title={translate('walletPage.deleteAccount')}
+ prompt={translate('walletPage.deleteConfirmation')}
+ confirmText={translate('common.delete')}
+ cancelText={translate('common.cancel')}
+ shouldShowCancelButton
+ danger
+ onModalHide={resetSelectedPaymentMethodData}
+ />
)}
diff --git a/src/pages/signin/ChangeExpensifyLoginLink.js b/src/pages/signin/ChangeExpensifyLoginLink.js
index 0fee34f988a1..6ba9b7fcd0f5 100755
--- a/src/pages/signin/ChangeExpensifyLoginLink.js
+++ b/src/pages/signin/ChangeExpensifyLoginLink.js
@@ -6,8 +6,8 @@ import _ from 'underscore';
import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
import Text from '@components/Text';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
-import useThemeStyles from '@styles/useThemeStyles';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/signin/ChooseSSOOrMagicCode.js b/src/pages/signin/ChooseSSOOrMagicCode.js
index bfb704b71f6d..db985e525545 100644
--- a/src/pages/signin/ChooseSSOOrMagicCode.js
+++ b/src/pages/signin/ChooseSSOOrMagicCode.js
@@ -8,10 +8,10 @@ import FormHelpMessage from '@components/FormHelpMessage';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as ErrorUtils from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Session from '@userActions/Session';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/signin/EmailDeliveryFailurePage.js b/src/pages/signin/EmailDeliveryFailurePage.js
index fe39618e79df..9996374cc6cd 100644
--- a/src/pages/signin/EmailDeliveryFailurePage.js
+++ b/src/pages/signin/EmailDeliveryFailurePage.js
@@ -8,7 +8,7 @@ import Text from '@components/Text';
import TextLink from '@components/TextLink';
import useKeyboardState from '@hooks/useKeyboardState';
import useLocalize from '@hooks/useLocalize';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import redirectToSignIn from '@userActions/SignInRedirect';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/signin/Licenses.js b/src/pages/signin/Licenses.js
index ef9cd8548a2d..605cfed328b5 100644
--- a/src/pages/signin/Licenses.js
+++ b/src/pages/signin/Licenses.js
@@ -4,7 +4,7 @@ import LocalePicker from '@components/LocalePicker';
import Text from '@components/Text';
import TextLink from '@components/TextLink';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
const currentYear = new Date().getFullYear();
diff --git a/src/pages/signin/LoginForm/BaseLoginForm.js b/src/pages/signin/LoginForm/BaseLoginForm.js
index e7ac249582a9..882f142ee5ad 100644
--- a/src/pages/signin/LoginForm/BaseLoginForm.js
+++ b/src/pages/signin/LoginForm/BaseLoginForm.js
@@ -18,6 +18,7 @@ import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import withToggleVisibilityView from '@components/withToggleVisibilityView';
import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions';
import usePrevious from '@hooks/usePrevious';
+import useThemeStyles from '@hooks/useThemeStyles';
import canFocusInputOnScreenFocus from '@libs/canFocusInputOnScreenFocus';
import compose from '@libs/compose';
import * as ErrorUtils from '@libs/ErrorUtils';
@@ -27,7 +28,6 @@ import * as LoginUtils from '@libs/LoginUtils';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as ValidationUtils from '@libs/ValidationUtils';
import Visibility from '@libs/Visibility';
-import useThemeStyles from '@styles/useThemeStyles';
import * as CloseAccount from '@userActions/CloseAccount';
import * as MemoryOnlyKeys from '@userActions/MemoryOnlyKeys/MemoryOnlyKeys';
import * as Session from '@userActions/Session';
@@ -52,7 +52,7 @@ const propTypes = {
/** Success message to display when necessary */
success: PropTypes.string,
- /** Whether or not a sign on form is loading (being submitted) */
+ /** Whether a sign on form is loading (being submitted) */
isLoading: PropTypes.bool,
}),
@@ -239,7 +239,7 @@ function LoginForm(props) {
const formErrorText = useMemo(() => (formError ? translate(formError) : ''), [formError, translate]);
const serverErrorText = useMemo(() => ErrorUtils.getLatestErrorMessage(props.account), [props.account]);
- const hasError = !_.isEmpty(serverErrorText);
+ const shouldShowServerError = !_.isEmpty(serverErrorText) && _.isEmpty(formErrorText);
return (
<>
@@ -270,7 +270,7 @@ function LoginForm(props) {
autoCorrect={false}
inputMode={CONST.INPUT_MODE.EMAIL}
errorText={formErrorText}
- hasError={hasError}
+ hasError={shouldShowServerError}
maxLength={CONST.LOGIN_CHARACTER_LIMIT}
/>
@@ -287,14 +287,14 @@ function LoginForm(props) {
// We need to unmount the submit button when the component is not visible so that the Enter button
// key handler gets unsubscribed
props.isVisible && (
-
+
{
diff --git a/src/pages/signin/SignInHeroCopy.js b/src/pages/signin/SignInHeroCopy.js
index 75c53c6b2344..847de3868cee 100644
--- a/src/pages/signin/SignInHeroCopy.js
+++ b/src/pages/signin/SignInHeroCopy.js
@@ -4,9 +4,9 @@ import {View} from 'react-native';
import Text from '@components/Text';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
const propTypes = {
diff --git a/src/pages/signin/SignInHeroImage.js b/src/pages/signin/SignInHeroImage.js
index 302e09a7afbc..c665a09932c2 100644
--- a/src/pages/signin/SignInHeroImage.js
+++ b/src/pages/signin/SignInHeroImage.js
@@ -3,7 +3,7 @@ import React from 'react';
import Lottie from '@components/Lottie';
import LottieAnimations from '@components/LottieAnimations';
import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
const propTypes = {
diff --git a/src/pages/signin/SignInModal.js b/src/pages/signin/SignInModal.js
index 9784eb0b67a1..1bb8b6065a15 100644
--- a/src/pages/signin/SignInModal.js
+++ b/src/pages/signin/SignInModal.js
@@ -1,8 +1,8 @@
import React from 'react';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
+import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Session from '@userActions/Session';
import SignInPage from './SignInPage';
diff --git a/src/pages/signin/SignInPage.js b/src/pages/signin/SignInPage.js
index dbad6324c922..8cb0ef9907af 100644
--- a/src/pages/signin/SignInPage.js
+++ b/src/pages/signin/SignInPage.js
@@ -5,19 +5,19 @@ import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import ColorSchemeWrapper from '@components/ColorSchemeWrapper';
-import CustomStatusBar from '@components/CustomStatusBar';
+import CustomStatusBarAndBackground from '@components/CustomStatusBarAndBackground';
+import ThemeProvider from '@components/ThemeProvider';
+import ThemeStylesProvider from '@components/ThemeStylesProvider';
import useLocalize from '@hooks/useLocalize';
import useSafeAreaInsets from '@hooks/useSafeAreaInsets';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as ActiveClientManager from '@libs/ActiveClientManager';
import * as Localize from '@libs/Localize';
import Log from '@libs/Log';
import Navigation from '@libs/Navigation/Navigation';
import Performance from '@libs/Performance';
-import ThemeProvider from '@styles/themes/ThemeProvider';
-import ThemeStylesProvider from '@styles/ThemeStylesProvider';
-import useStyleUtils from '@styles/useStyleUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import * as App from '@userActions/App';
import * as Session from '@userActions/Session';
import CONST from '@src/CONST';
@@ -288,7 +288,7 @@ function SignInPage(props) {
-
+ {
+ const rootParentReport = ReportUtils.getRootParentReport(report);
+ return `${ONYXKEYS.COLLECTION.POLICY}${rootParentReport ? rootParentReport.policyID : '0'}`;
+ },
+ selector: (policy) => _.pick(policy, ['role']),
+ },
}),
)(TaskAssigneeSelectorModal);
diff --git a/src/pages/tasks/TaskDescriptionPage.js b/src/pages/tasks/TaskDescriptionPage.js
index 4e963a22b749..3a6999d4408a 100644
--- a/src/pages/tasks/TaskDescriptionPage.js
+++ b/src/pages/tasks/TaskDescriptionPage.js
@@ -1,8 +1,11 @@
import {useFocusEffect} from '@react-navigation/native';
import ExpensiMark from 'expensify-common/lib/ExpensiMark';
+import lodashGet from 'lodash/get';
+import PropTypes from 'prop-types';
import React, {useCallback, useRef} from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
+import _ from 'underscore';
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
import FormProvider from '@components/Form/FormProvider';
import InputWrapper from '@components/Form/InputWrapper';
@@ -11,6 +14,7 @@ import ScreenWrapper from '@components/ScreenWrapper';
import TextInput from '@components/TextInput';
import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as Browser from '@libs/Browser';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
@@ -19,7 +23,6 @@ import StringUtils from '@libs/StringUtils';
import updateMultilineInputRange from '@libs/updateMultilineInputRange';
import withReportOrNotFound from '@pages/home/report/withReportOrNotFound';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Task from '@userActions/Task';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
@@ -28,12 +31,19 @@ const propTypes = {
/** The report currently being looked at */
report: reportPropTypes,
+ /** The policy of parent report */
+ rootParentReportPolicy: PropTypes.shape({
+ /** The role of current user */
+ role: PropTypes.string,
+ }),
+
/* Onyx Props */
...withLocalizePropTypes,
};
const defaultProps = {
report: {},
+ rootParentReportPolicy: {},
};
const parser = new ExpensiMark();
@@ -64,7 +74,7 @@ function TaskDescriptionPage(props) {
const focusTimeoutRef = useRef(null);
const isOpen = ReportUtils.isOpenTaskReport(props.report);
- const canModifyTask = Task.canModifyTask(props.report, props.currentUserPersonalDetails.accountID);
+ const canModifyTask = Task.canModifyTask(props.report, props.currentUserPersonalDetails.accountID, lodashGet(props.rootParentReportPolicy, 'role', ''));
const isTaskNonEditable = ReportUtils.isTaskReport(props.report) && (!canModifyTask || !isOpen);
useFocusEffect(
@@ -138,5 +148,12 @@ export default compose(
report: {
key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`,
},
+ rootParentReportPolicy: {
+ key: ({report}) => {
+ const rootParentReport = ReportUtils.getRootParentReport(report);
+ return `${ONYXKEYS.COLLECTION.POLICY}${rootParentReport ? rootParentReport.policyID : '0'}`;
+ },
+ selector: (policy) => _.pick(policy, ['role']),
+ },
}),
)(TaskDescriptionPage);
diff --git a/src/pages/tasks/TaskShareDestinationSelectorModal.js b/src/pages/tasks/TaskShareDestinationSelectorModal.js
index 914afa34bd85..b0b6dac15d69 100644
--- a/src/pages/tasks/TaskShareDestinationSelectorModal.js
+++ b/src/pages/tasks/TaskShareDestinationSelectorModal.js
@@ -10,6 +10,7 @@ import ScreenWrapper from '@components/ScreenWrapper';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import useAutoFocusInput from '@hooks/useAutoFocusInput';
import useNetwork from '@hooks/useNetwork';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as Report from '@libs/actions/Report';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
@@ -17,7 +18,6 @@ import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as ReportUtils from '@libs/ReportUtils';
import personalDetailsPropType from '@pages/personalDetailsPropType';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Task from '@userActions/Task';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/tasks/TaskTitlePage.js b/src/pages/tasks/TaskTitlePage.js
index 6dff0e3d8f7e..9b393a8a2374 100644
--- a/src/pages/tasks/TaskTitlePage.js
+++ b/src/pages/tasks/TaskTitlePage.js
@@ -1,3 +1,5 @@
+import lodashGet from 'lodash/get';
+import PropTypes from 'prop-types';
import React, {useCallback, useRef} from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
@@ -10,12 +12,12 @@ import ScreenWrapper from '@components/ScreenWrapper';
import TextInput from '@components/TextInput';
import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
import * as ReportUtils from '@libs/ReportUtils';
import withReportOrNotFound from '@pages/home/report/withReportOrNotFound';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Task from '@userActions/Task';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
@@ -24,12 +26,19 @@ const propTypes = {
/** The report currently being looked at */
report: reportPropTypes,
+ /** The policy of parent report */
+ rootParentReportPolicy: PropTypes.shape({
+ /** The role of current user */
+ role: PropTypes.string,
+ }),
+
/* Onyx Props */
...withLocalizePropTypes,
};
const defaultProps = {
report: {},
+ rootParentReportPolicy: {},
};
function TaskTitlePage(props) {
@@ -70,7 +79,7 @@ function TaskTitlePage(props) {
const inputRef = useRef(null);
const isOpen = ReportUtils.isOpenTaskReport(props.report);
- const canModifyTask = Task.canModifyTask(props.report, props.currentUserPersonalDetails.accountID);
+ const canModifyTask = Task.canModifyTask(props.report, props.currentUserPersonalDetails.accountID, lodashGet(props.rootParentReportPolicy, 'role', ''));
const isTaskNonEditable = ReportUtils.isTaskReport(props.report) && (!canModifyTask || !isOpen);
return (
@@ -130,5 +139,12 @@ export default compose(
report: {
key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`,
},
+ rootParentReportPolicy: {
+ key: ({report}) => {
+ const rootParentReport = ReportUtils.getRootParentReport(report);
+ return `${ONYXKEYS.COLLECTION.POLICY}${rootParentReport ? rootParentReport.policyID : '0'}`;
+ },
+ selector: (policy) => _.pick(policy, ['role']),
+ },
}),
)(TaskTitlePage);
diff --git a/src/pages/workspace/WorkspaceInitialPage.js b/src/pages/workspace/WorkspaceInitialPage.js
index 3c73687ca813..80813c847239 100644
--- a/src/pages/workspace/WorkspaceInitialPage.js
+++ b/src/pages/workspace/WorkspaceInitialPage.js
@@ -18,6 +18,7 @@ import Tooltip from '@components/Tooltip';
import useLocalize from '@hooks/useLocalize';
import usePrevious from '@hooks/usePrevious';
import useSingleExecution from '@hooks/useSingleExecution';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWaitForNavigation from '@hooks/useWaitForNavigation';
import useWindowDimensions from '@hooks/useWindowDimensions';
import compose from '@libs/compose';
@@ -26,7 +27,6 @@ import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReportUtils from '@libs/ReportUtils';
import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimbursementAccountPropTypes';
import reportPropTypes from '@pages/reportPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as App from '@userActions/App';
import * as Policy from '@userActions/Policy';
import * as ReimbursementAccount from '@userActions/ReimbursementAccount';
diff --git a/src/pages/workspace/WorkspaceInvitePage.js b/src/pages/workspace/WorkspaceInvitePage.js
index 3528224f39b9..16da273750fa 100644
--- a/src/pages/workspace/WorkspaceInvitePage.js
+++ b/src/pages/workspace/WorkspaceInvitePage.js
@@ -11,12 +11,12 @@ import ScreenWrapper from '@components/ScreenWrapper';
import SelectionList from '@components/SelectionList';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as Browser from '@libs/Browser';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as PolicyUtils from '@libs/PolicyUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Policy from '@userActions/Policy';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
@@ -225,7 +225,7 @@ function WorkspaceInvitePage(props) {
if (usersToInvite.length === 0 && CONST.EXPENSIFY_EMAILS.includes(searchValue)) {
return translate('messages.errorMessageInvalidEmail');
}
- if (usersToInvite.length === 0 && excludedUsers.includes(searchValue)) {
+ if (usersToInvite.length === 0 && excludedUsers.includes(OptionsListUtils.addSMSDomainIfPhoneNumber(searchValue))) {
return translate('messages.userIsAlreadyMember', {login: searchValue, name: policyName});
}
return OptionsListUtils.getHeaderMessage(personalDetails.length !== 0, usersToInvite.length > 0, searchValue);
diff --git a/src/pages/workspace/WorkspaceMembersPage.js b/src/pages/workspace/WorkspaceMembersPage.js
index 59993087c44c..9834d4e9e1c0 100644
--- a/src/pages/workspace/WorkspaceMembersPage.js
+++ b/src/pages/workspace/WorkspaceMembersPage.js
@@ -19,6 +19,7 @@ import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultPro
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions';
import usePrevious from '@hooks/usePrevious';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as Browser from '@libs/Browser';
import compose from '@libs/compose';
import Log from '@libs/Log';
@@ -28,7 +29,6 @@ import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as UserUtils from '@libs/UserUtils';
import personalDetailsPropType from '@pages/personalDetailsPropType';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Policy from '@userActions/Policy';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/workspace/WorkspaceNewRoomPage.js b/src/pages/workspace/WorkspaceNewRoomPage.js
index c7fc9de2571f..6f86e871e8ae 100644
--- a/src/pages/workspace/WorkspaceNewRoomPage.js
+++ b/src/pages/workspace/WorkspaceNewRoomPage.js
@@ -3,8 +3,12 @@ import React, {useCallback, useEffect, useMemo, useState} from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
+import BlockingView from '@components/BlockingViews/BlockingView';
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
-import Form from '@components/Form';
+import Button from '@components/Button';
+import FormProvider from '@components/Form/FormProvider';
+import InputWrapper from '@components/Form/InputWrapper';
+import * as Illustrations from '@components/Icon/Illustrations';
import KeyboardAvoidingView from '@components/KeyboardAvoidingView';
import OfflineIndicator from '@components/OfflineIndicator';
import RoomNameInput from '@components/RoomNameInput';
@@ -17,6 +21,7 @@ import useAutoFocusInput from '@hooks/useAutoFocusInput';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import usePrevious from '@hooks/usePrevious';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import compose from '@libs/compose';
import * as ErrorUtils from '@libs/ErrorUtils';
@@ -25,12 +30,11 @@ import Permissions from '@libs/Permissions';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReportUtils from '@libs/ReportUtils';
import * as ValidationUtils from '@libs/ValidationUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import variables from '@styles/variables';
-import * as App from '@userActions/App';
import * as Report from '@userActions/Report';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
+import ROUTES from '@src/ROUTES';
const propTypes = {
/** All reports shared with the user */
@@ -190,7 +194,15 @@ function WorkspaceNewRoomPage(props) {
[props.reports],
);
- const workspaceOptions = useMemo(() => _.map(PolicyUtils.getActivePolicies(props.policies), (policy) => ({label: policy.name, key: policy.id, value: policy.id})), [props.policies]);
+ const workspaceOptions = useMemo(
+ () =>
+ _.map(PolicyUtils.getActivePolicies(props.policies), (policy) => ({
+ label: policy.name,
+ key: policy.id,
+ value: policy.id,
+ })),
+ [props.policies],
+ );
const writeCapabilityOptions = useMemo(
() =>
@@ -216,13 +228,28 @@ function WorkspaceNewRoomPage(props) {
const {inputCallbackRef} = useAutoFocusInput();
+ const renderEmptyWorkspaceView = () => (
+ <>
+
+ Navigation.navigate(ROUTES.SETTINGS_WORKSPACES)}
+ style={[styles.mh5, styles.mb5]}
+ />
+ {isSmallScreenWidth && }
+ >
+ );
+
return (
- App.createWorkspaceWithPolicyDraftAndNavigateToIt()}
- >
+
- {({insets}) => (
-
-
- {isSmallScreenWidth && }
-
- )}
+ {visibilityDescription}
+
+ {isSmallScreenWidth && }
+
+ )
+ }
);
diff --git a/src/pages/workspace/WorkspacePageWithSections.js b/src/pages/workspace/WorkspacePageWithSections.js
index bea94ed3ef4e..6b5c179a2e51 100644
--- a/src/pages/workspace/WorkspacePageWithSections.js
+++ b/src/pages/workspace/WorkspacePageWithSections.js
@@ -9,13 +9,13 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import ScrollViewWithContext from '@components/ScrollViewWithContext';
import useNetwork from '@hooks/useNetwork';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import BankAccount from '@libs/models/BankAccount';
import Navigation from '@libs/Navigation/Navigation';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimbursementAccountPropTypes';
import userPropTypes from '@pages/settings/userPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as BankAccounts from '@userActions/BankAccounts';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
diff --git a/src/pages/workspace/WorkspaceResetBankAccountModal.js b/src/pages/workspace/WorkspaceResetBankAccountModal.js
index 19e15a2828bc..f4ad662ebb75 100644
--- a/src/pages/workspace/WorkspaceResetBankAccountModal.js
+++ b/src/pages/workspace/WorkspaceResetBankAccountModal.js
@@ -5,9 +5,9 @@ import {withOnyx} from 'react-native-onyx';
import ConfirmModal from '@components/ConfirmModal';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import BankAccount from '@libs/models/BankAccount';
import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimbursementAccountPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as BankAccounts from '@userActions/BankAccounts';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/workspace/WorkspaceSettingsPage.js b/src/pages/workspace/WorkspaceSettingsPage.js
index d121134f26da..9bc4b755fce9 100644
--- a/src/pages/workspace/WorkspaceSettingsPage.js
+++ b/src/pages/workspace/WorkspaceSettingsPage.js
@@ -16,12 +16,12 @@ import Text from '@components/Text';
import TextInput from '@components/TextInput';
import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
import * as ReportUtils from '@libs/ReportUtils';
import * as UserUtils from '@libs/UserUtils';
import * as ValidationUtils from '@libs/ValidationUtils';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Policy from '@userActions/Policy';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/workspace/WorkspacesListPage.js b/src/pages/workspace/WorkspacesListPage.js
index cf258b462285..cac0d1bd71a6 100755
--- a/src/pages/workspace/WorkspacesListPage.js
+++ b/src/pages/workspace/WorkspacesListPage.js
@@ -12,6 +12,8 @@ import MenuItem from '@components/MenuItem';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import * as CurrencyUtils from '@libs/CurrencyUtils';
import Navigation from '@libs/Navigation/Navigation';
@@ -19,8 +21,6 @@ import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReportUtils from '@libs/ReportUtils';
import policyMemberPropType from '@pages/policyMemberPropType';
import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimbursementAccountPropTypes';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import * as App from '@userActions/App';
import * as Policy from '@userActions/Policy';
import CONST from '@src/CONST';
diff --git a/src/pages/workspace/bills/WorkspaceBillsFirstSection.js b/src/pages/workspace/bills/WorkspaceBillsFirstSection.js
index 8221beb23fcf..3f7f59180c8b 100644
--- a/src/pages/workspace/bills/WorkspaceBillsFirstSection.js
+++ b/src/pages/workspace/bills/WorkspaceBillsFirstSection.js
@@ -10,9 +10,9 @@ import Section from '@components/Section';
import Text from '@components/Text';
import TextLink from '@components/TextLink';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import userPropTypes from '@pages/settings/userPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Link from '@userActions/Link';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/workspace/bills/WorkspaceBillsNoVBAView.js b/src/pages/workspace/bills/WorkspaceBillsNoVBAView.js
index 51faa2710208..8211f70163d5 100644
--- a/src/pages/workspace/bills/WorkspaceBillsNoVBAView.js
+++ b/src/pages/workspace/bills/WorkspaceBillsNoVBAView.js
@@ -6,7 +6,7 @@ import * as Illustrations from '@components/Icon/Illustrations';
import Section from '@components/Section';
import Text from '@components/Text';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import WorkspaceBillsFirstSection from './WorkspaceBillsFirstSection';
const propTypes = {
diff --git a/src/pages/workspace/bills/WorkspaceBillsVBAView.js b/src/pages/workspace/bills/WorkspaceBillsVBAView.js
index 065c3fbaa84d..dd9c1c7fbaf6 100644
--- a/src/pages/workspace/bills/WorkspaceBillsVBAView.js
+++ b/src/pages/workspace/bills/WorkspaceBillsVBAView.js
@@ -6,7 +6,7 @@ import * as Illustrations from '@components/Icon/Illustrations';
import Section from '@components/Section';
import Text from '@components/Text';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as Link from '@userActions/Link';
import WorkspaceBillsFirstSection from './WorkspaceBillsFirstSection';
diff --git a/src/pages/workspace/card/WorkspaceCardNoVBAView.js b/src/pages/workspace/card/WorkspaceCardNoVBAView.js
index cf920571495f..3233f8ea7e23 100644
--- a/src/pages/workspace/card/WorkspaceCardNoVBAView.js
+++ b/src/pages/workspace/card/WorkspaceCardNoVBAView.js
@@ -7,7 +7,7 @@ import Section from '@components/Section';
import Text from '@components/Text';
import UnorderedList from '@components/UnorderedList';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
const propTypes = {
/** The policy ID currently being configured */
diff --git a/src/pages/workspace/card/WorkspaceCardVBANoECardView.js b/src/pages/workspace/card/WorkspaceCardVBANoECardView.js
index 2b4925ff20bd..970cd9105368 100644
--- a/src/pages/workspace/card/WorkspaceCardVBANoECardView.js
+++ b/src/pages/workspace/card/WorkspaceCardVBANoECardView.js
@@ -8,9 +8,9 @@ import Section from '@components/Section';
import Text from '@components/Text';
import UnorderedList from '@components/UnorderedList';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import userPropTypes from '@pages/settings/userPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Link from '@userActions/Link';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/pages/workspace/card/WorkspaceCardVBAWithECardView.js b/src/pages/workspace/card/WorkspaceCardVBAWithECardView.js
index 278f6d01319c..40ecd80b8e6e 100644
--- a/src/pages/workspace/card/WorkspaceCardVBAWithECardView.js
+++ b/src/pages/workspace/card/WorkspaceCardVBAWithECardView.js
@@ -6,7 +6,7 @@ import Section from '@components/Section';
import Text from '@components/Text';
import UnorderedList from '@components/UnorderedList';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as Link from '@userActions/Link';
const propTypes = {
diff --git a/src/pages/workspace/invoices/WorkspaceInvoicesFirstSection.js b/src/pages/workspace/invoices/WorkspaceInvoicesFirstSection.js
index 749e6b28b11b..b33311021e61 100644
--- a/src/pages/workspace/invoices/WorkspaceInvoicesFirstSection.js
+++ b/src/pages/workspace/invoices/WorkspaceInvoicesFirstSection.js
@@ -6,7 +6,7 @@ import * as Illustrations from '@components/Icon/Illustrations';
import Section from '@components/Section';
import Text from '@components/Text';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as Link from '@userActions/Link';
const propTypes = {
diff --git a/src/pages/workspace/invoices/WorkspaceInvoicesNoVBAView.js b/src/pages/workspace/invoices/WorkspaceInvoicesNoVBAView.js
index 9be8994bc5a7..25a97ac93442 100644
--- a/src/pages/workspace/invoices/WorkspaceInvoicesNoVBAView.js
+++ b/src/pages/workspace/invoices/WorkspaceInvoicesNoVBAView.js
@@ -6,7 +6,7 @@ import * as Illustrations from '@components/Icon/Illustrations';
import Section from '@components/Section';
import Text from '@components/Text';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import WorkspaceInvoicesFirstSection from './WorkspaceInvoicesFirstSection';
const propTypes = {
diff --git a/src/pages/workspace/invoices/WorkspaceInvoicesVBAView.js b/src/pages/workspace/invoices/WorkspaceInvoicesVBAView.js
index 0c6ea7103990..c5e047f6291b 100644
--- a/src/pages/workspace/invoices/WorkspaceInvoicesVBAView.js
+++ b/src/pages/workspace/invoices/WorkspaceInvoicesVBAView.js
@@ -6,7 +6,7 @@ import * as Illustrations from '@components/Icon/Illustrations';
import Section from '@components/Section';
import Text from '@components/Text';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as Link from '@userActions/Link';
import WorkspaceInvoicesFirstSection from './WorkspaceInvoicesFirstSection';
diff --git a/src/pages/workspace/reimburse/WorkspaceReimburseSection.js b/src/pages/workspace/reimburse/WorkspaceReimburseSection.js
index bf9111775f18..7cf17c9d498c 100644
--- a/src/pages/workspace/reimburse/WorkspaceReimburseSection.js
+++ b/src/pages/workspace/reimburse/WorkspaceReimburseSection.js
@@ -9,10 +9,10 @@ import * as Illustrations from '@components/Icon/Illustrations';
import networkPropTypes from '@components/networkPropTypes';
import Section from '@components/Section';
import Text from '@components/Text';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import BankAccount from '@libs/models/BankAccount';
import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimbursementAccountPropTypes';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
import * as Link from '@userActions/Link';
import CONST from '@src/CONST';
diff --git a/src/pages/workspace/reimburse/WorkspaceReimburseView.js b/src/pages/workspace/reimburse/WorkspaceReimburseView.js
index 752639c33d3a..23136064fc2b 100644
--- a/src/pages/workspace/reimburse/WorkspaceReimburseView.js
+++ b/src/pages/workspace/reimburse/WorkspaceReimburseView.js
@@ -14,11 +14,11 @@ import {withNetwork} from '@components/OnyxProvider';
import Section from '@components/Section';
import Text from '@components/Text';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimbursementAccountPropTypes';
-import useThemeStyles from '@styles/useThemeStyles';
import * as BankAccounts from '@userActions/BankAccounts';
import * as Link from '@userActions/Link';
import * as Policy from '@userActions/Policy';
diff --git a/src/pages/workspace/travel/WorkspaceTravelNoVBAView.js b/src/pages/workspace/travel/WorkspaceTravelNoVBAView.js
index 64269084aad4..d8154955a5d7 100644
--- a/src/pages/workspace/travel/WorkspaceTravelNoVBAView.js
+++ b/src/pages/workspace/travel/WorkspaceTravelNoVBAView.js
@@ -6,7 +6,7 @@ import * as Illustrations from '@components/Icon/Illustrations';
import Section from '@components/Section';
import Text from '@components/Text';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
const propTypes = {
/** The policy ID currently being configured */
diff --git a/src/pages/workspace/travel/WorkspaceTravelVBAView.js b/src/pages/workspace/travel/WorkspaceTravelVBAView.js
index b6424e918037..5228f9185852 100644
--- a/src/pages/workspace/travel/WorkspaceTravelVBAView.js
+++ b/src/pages/workspace/travel/WorkspaceTravelVBAView.js
@@ -5,7 +5,7 @@ import * as Illustrations from '@components/Icon/Illustrations';
import Section from '@components/Section';
import Text from '@components/Text';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
-import useThemeStyles from '@styles/useThemeStyles';
+import useThemeStyles from '@hooks/useThemeStyles';
import * as Link from '@userActions/Link';
import * as Report from '@userActions/Report';
import CONST from '@src/CONST';
diff --git a/src/stories/CheckboxWithLabel.stories.js b/src/stories/CheckboxWithLabel.stories.js
index e8d45f434b16..2da4713e81b3 100644
--- a/src/stories/CheckboxWithLabel.stories.js
+++ b/src/stories/CheckboxWithLabel.stories.js
@@ -1,7 +1,7 @@
import React from 'react';
import CheckboxWithLabel from '@components/CheckboxWithLabel';
import Text from '@components/Text';
-import styles from '@styles/styles';
+import {defaultStyles} from '@styles/index';
/**
* We use the Component Story Format for writing stories. Follow the docs here:
@@ -34,9 +34,9 @@ WithLabelComponent.args = {
onInputChange: () => {},
LabelComponent: () => (
<>
- Test
- Test
- Test
+ Test
+ Test
+ Test
>
),
};
diff --git a/src/stories/Composer.stories.js b/src/stories/Composer.stories.js
index 799012e1c051..04fa9be7de86 100644
--- a/src/stories/Composer.stories.js
+++ b/src/stories/Composer.stories.js
@@ -5,9 +5,9 @@ import Composer from '@components/Composer';
import RenderHTML from '@components/RenderHTML';
import Text from '@components/Text';
import withNavigationFallback from '@components/withNavigationFallback';
-import styles from '@styles/styles';
-import themeColors from '@styles/themes/default';
-import useStyleUtils from '@styles/useStyleUtils';
+import useStyleUtils from '@hooks/useStyleUtils';
+import {defaultStyles} from '@styles/index';
+import {defaultTheme} from '@styles/theme';
import CONST from '@src/CONST';
const ComposerWithNavigation = withNavigationFallback(Composer);
@@ -32,29 +32,29 @@ function Default(args) {
return (
-
+
-
+
- Entered Comment (Drop Enabled)
+ Entered Comment (Drop Enabled){comment}
-
- Rendered Comment
+
+ Rendered Comment
{Boolean(renderedHTML) && }
{Boolean(pastedFile) && (
-
+
-
+
{fileURL ? (
-
+ Release to upload file
diff --git a/src/stories/Form.stories.js b/src/stories/Form.stories.js
index 1edc860a4871..a937c6732e9b 100644
--- a/src/stories/Form.stories.js
+++ b/src/stories/Form.stories.js
@@ -11,7 +11,7 @@ import Text from '@components/Text';
import TextInput from '@components/TextInput';
import NetworkConnection from '@libs/NetworkConnection';
import * as ValidationUtils from '@libs/ValidationUtils';
-import styles from '@styles/styles';
+import {defaultStyles} from '@styles/index';
import * as FormActions from '@userActions/FormActions';
import CONST from '@src/CONST';
@@ -60,26 +60,26 @@ function Template(args) {
label="Account number"
accessibilityLabel="Account number"
inputID="accountNumber"
- containerStyles={[styles.mt4]}
+ containerStyles={[defaultStyles.mt4]}
/>
-
+ I accept the Expensify Terms of Service}
/>
diff --git a/src/stories/MenuItem.stories.js b/src/stories/MenuItem.stories.js
index 47b74b1f0ef8..0e7260fa4d1a 100644
--- a/src/stories/MenuItem.stories.js
+++ b/src/stories/MenuItem.stories.js
@@ -128,6 +128,17 @@ BrickRoadIndicatorFailure.args = {
brickRoadIndicator: 'error',
};
+const ErrorMessage = Template.bind({});
+ErrorMessage.args = {
+ title: 'Alberta Bobbeth Charleson',
+ icon: Chase,
+ iconHeight: variables.iconSizeExtraLarge,
+ iconWidth: variables.iconSizeExtraLarge,
+ shouldShowRightIcon: true,
+ errorText: 'Error text which describes the error',
+ brickRoadIndicator: 'error',
+};
+
export default story;
export {
Default,
@@ -141,4 +152,5 @@ export {
BrickRoadIndicatorSuccess,
BrickRoadIndicatorFailure,
RightIconAndDescriptionWithLabel,
+ ErrorMessage,
};
diff --git a/src/stories/PopoverMenu.stories.js b/src/stories/PopoverMenu.stories.js
index b860a8343309..c03a554741f1 100644
--- a/src/stories/PopoverMenu.stories.js
+++ b/src/stories/PopoverMenu.stories.js
@@ -3,7 +3,8 @@ import {SafeAreaProvider} from 'react-native-safe-area-context';
import * as Expensicons from '@components/Icon/Expensicons';
import MenuItem from '@components/MenuItem';
import PopoverMenu from '@components/PopoverMenu';
-import themeColors from '@styles/themes/default';
+// eslint-disable-next-line no-restricted-imports
+import themeColors from '@styles/theme/themes/dark';
/**
* We use the Component Story Format for writing stories. Follow the docs here:
diff --git a/src/stories/Search.stories.tsx b/src/stories/Search.stories.tsx
new file mode 100644
index 000000000000..a501fc2610b0
--- /dev/null
+++ b/src/stories/Search.stories.tsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import Search, {SearchProps} from '@components/Search';
+
+/**
+ * We use the Component Story Format for writing stories. Follow the docs here:
+ *
+ * https://storybook.js.org/docs/react/writing-stories/introduction#component-story-format
+ */
+const story = {
+ title: 'Components/Search',
+ component: Search,
+};
+
+type StoryType = typeof Template & {args?: Partial};
+
+function Template(args: SearchProps) {
+ // eslint-disable-next-line react/jsx-props-no-spreading
+ return ;
+}
+
+// Arguments can be passed to the component by binding
+// See: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
+const Default: StoryType = Template.bind({});
+Default.args = {
+ onPress: () => alert('Pressed'),
+};
+
+const CustomPlaceholderAndTooltip: StoryType = Template.bind({});
+CustomPlaceholderAndTooltip.args = {
+ placeholder: 'Search for a specific thing...',
+ tooltip: 'Custom tooltip text',
+ onPress: () => alert('This component has custom placeholder text. Also custom tooltip text when hovered.'),
+};
+
+const CustomBackground: StoryType = Template.bind({});
+CustomBackground.args = {
+ onPress: () => alert('This component has custom styles applied'),
+ style: {backgroundColor: 'darkgreen'},
+};
+
+export default story;
+export {Default, CustomPlaceholderAndTooltip, CustomBackground};
diff --git a/src/stories/SelectionList.stories.js b/src/stories/SelectionList.stories.js
index ec80a8e908ac..c08ef2df783a 100644
--- a/src/stories/SelectionList.stories.js
+++ b/src/stories/SelectionList.stories.js
@@ -3,7 +3,7 @@ import {View} from 'react-native';
import _ from 'underscore';
import SelectionList from '@components/SelectionList';
import Text from '@components/Text';
-import styles from '@styles/styles';
+import {defaultStyles} from '@styles/index';
import CONST from '@src/CONST';
/**
@@ -228,8 +228,8 @@ function MultipleSelection(args) {
accountID: item.keyForList,
login: item.text,
rightElement: isAdmin && (
-
- Admin
+
+ Admin
),
};
@@ -290,8 +290,8 @@ function WithSectionHeader(args) {
accountID: item.keyForList,
login: item.text,
rightElement: isAdmin && (
-
- Admin
+
+ Admin
),
};
@@ -350,8 +350,8 @@ function WithConfirmButton(args) {
accountID: item.keyForList,
login: item.text,
rightElement: isAdmin && (
-
- Admin
+
+ Admin
),
};
diff --git a/src/styles/illustrations/ThemeIllustrationsContext.ts b/src/styles/illustrations/ThemeIllustrationsContext.ts
deleted file mode 100644
index e4956e6c0479..000000000000
--- a/src/styles/illustrations/ThemeIllustrationsContext.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import React from 'react';
-import DarkIllustrations from './dark';
-import {Illustrations} from './types';
-
-const ThemeIllustrationsContext = React.createContext(DarkIllustrations);
-
-export default ThemeIllustrationsContext;
diff --git a/src/styles/styles.ts b/src/styles/index.ts
similarity index 96%
rename from src/styles/styles.ts
rename to src/styles/index.ts
index fb851ecfcf54..0effb7db2ab1 100644
--- a/src/styles/styles.ts
+++ b/src/styles/index.ts
@@ -2,42 +2,43 @@
import {LineLayerStyleProps} from '@rnmapbox/maps/src/utils/MapboxStyles';
import lodashClamp from 'lodash/clamp';
import {LineLayer} from 'react-map-gl';
-import {AnimatableNumericValue, Animated, ImageStyle, TextStyle, ViewStyle} from 'react-native';
+import {AnimatableNumericValue, Animated, ImageStyle, StyleSheet, TextStyle, ViewStyle} from 'react-native';
import {CustomAnimation} from 'react-native-animatable';
import {PickerStyle} from 'react-native-picker-select';
import {MixedStyleDeclaration, MixedStyleRecord} from 'react-native-render-html';
import DotLottieAnimation from '@components/LottieAnimations/types';
import * as Browser from '@libs/Browser';
import CONST from '@src/CONST';
-import addOutlineWidth from './addOutlineWidth';
-import codeStyles from './codeStyles';
-import colors from './colors';
-import fontFamily from './fontFamily';
-import fontWeightBold from './fontWeight/bold';
-import getPopOverVerticalOffset from './getPopOverVerticalOffset';
-import optionAlternateTextPlatformStyles from './optionAlternateTextPlatformStyles';
-import overflowXHidden from './overflowXHidden';
-import pointerEventsAuto from './pointerEventsAuto';
-import pointerEventsBoxNone from './pointerEventsBoxNone';
-import pointerEventsNone from './pointerEventsNone';
-import {defaultTheme} from './themes/themes';
-import {type ThemeColors} from './themes/types';
-import borders from './utilities/borders';
-import cursor from './utilities/cursor';
-import display from './utilities/display';
-import flex from './utilities/flex';
-import objectFit from './utilities/objectFit';
-import overflow from './utilities/overflow';
-import positioning from './utilities/positioning';
-import sizing from './utilities/sizing';
-import spacing from './utilities/spacing';
-import textDecorationLine from './utilities/textDecorationLine';
-import textUnderline from './utilities/textUnderline';
-import userSelect from './utilities/userSelect';
-import visibility from './utilities/visibility';
-import whiteSpace from './utilities/whiteSpace';
-import wordBreak from './utilities/wordBreak';
-import writingDirection from './utilities/writingDirection';
+import {defaultTheme} from './theme';
+import colors from './theme/colors';
+import {type ThemeColors} from './theme/types';
+import addOutlineWidth from './utils/addOutlineWidth';
+import borders from './utils/borders';
+import codeStyles from './utils/codeStyles';
+import cursor from './utils/cursor';
+import display from './utils/display';
+import editedLabelStyles from './utils/editedLabelStyles';
+import flex from './utils/flex';
+import fontFamily from './utils/fontFamily';
+import fontWeightBold from './utils/fontWeight/bold';
+import getPopOverVerticalOffset from './utils/getPopOverVerticalOffset';
+import objectFit from './utils/objectFit';
+import optionAlternateTextPlatformStyles from './utils/optionAlternateTextPlatformStyles';
+import overflow from './utils/overflow';
+import overflowXHidden from './utils/overflowXHidden';
+import pointerEventsAuto from './utils/pointerEventsAuto';
+import pointerEventsBoxNone from './utils/pointerEventsBoxNone';
+import pointerEventsNone from './utils/pointerEventsNone';
+import positioning from './utils/positioning';
+import sizing from './utils/sizing';
+import spacing from './utils/spacing';
+import textDecorationLine from './utils/textDecorationLine';
+import textUnderline from './utils/textUnderline';
+import userSelect from './utils/userSelect';
+import visibility from './utils/visibility';
+import whiteSpace from './utils/whiteSpace';
+import wordBreak from './utils/wordBreak';
+import writingDirection from './utils/writingDirection';
import variables from './variables';
type ColorScheme = (typeof CONST.COLOR_SCHEME)[keyof typeof CONST.COLOR_SCHEME];
@@ -234,6 +235,7 @@ const styles = (theme: ThemeColors) =>
...textUnderline,
...objectFit,
...textDecorationLine,
+ editedLabelStyles,
autoCompleteSuggestionsContainer: {
backgroundColor: theme.appBG,
@@ -2422,6 +2424,11 @@ const styles = (theme: ThemeColors) =>
alignItems: 'center',
padding: 20,
},
+ numberPadWrapper: {
+ width: '100%',
+ alignItems: 'center',
+ paddingHorizontal: 20,
+ },
avatarSectionWrapper: {
width: '100%',
@@ -3021,6 +3028,31 @@ const styles = (theme: ThemeColors) =>
flex: 1,
},
+ searchPressable: {
+ height: 40,
+ },
+
+ searchContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ alignItems: 'center',
+ gap: 8,
+ paddingHorizontal: 24,
+ backgroundColor: theme.highlightBG,
+ borderRadius: variables.componentBorderRadiusRounded,
+ },
+
+ searchContainerHovered: {
+ backgroundColor: theme.border,
+ },
+
+ searchInputStyle: {
+ color: colors.productDark800,
+ fontSize: 13,
+ lineHeight: 16,
+ width: '100%',
+ },
+
threeDotsPopoverOffset: (windowWidth: number) =>
({
...getPopOverVerticalOffset(60),
@@ -3677,7 +3709,6 @@ const styles = (theme: ThemeColors) =>
alignItems: 'center',
paddingLeft: 10,
paddingRight: 4,
- marginBottom: 32,
alignSelf: 'flex-start',
...userSelect.userSelectNone,
},
@@ -3897,10 +3928,59 @@ const styles = (theme: ThemeColors) =>
fontSize: variables.fontSizeNormal,
marginRight: 4,
},
+ timePickerInput: {
+ fontSize: 69,
+ minWidth: 56,
+ alignSelf: 'center',
+ },
+ timePickerWidth100: {
+ width: 100,
+ },
+ timePickerHeight100: {
+ height: 100,
+ },
+ timePickerSemiDot: {
+ fontSize: 69,
+ height: 84,
+ alignSelf: 'center',
+ },
+ timePickerSwitcherContainer: {
+ flexDirection: 'row',
+ alignItems: 'flex-start',
+ justifyContent: 'center',
+ },
+ selectionListRadioSeparator: {
+ height: StyleSheet.hairlineWidth,
+ backgroundColor: theme.border,
+ marginHorizontal: 20,
+ },
+
draggableTopBar: {
height: 30,
width: '100%',
},
+ menuItemError: {
+ position: 'absolute',
+ bottom: -4,
+ left: 20,
+ right: 20,
+ },
+ formHelperMessage: {
+ height: 32,
+ },
+ timePickerInputExtraSmall: {
+ fontSize: 50,
+ },
+ setTimeFormButtonContainer: {
+ minHeight: 54,
+ },
+ timePickerInputsContainer: {
+ maxHeight: 100,
+ },
+ timePickerButtonErrorText: {
+ position: 'absolute',
+ top: -36,
+ },
chatBottomLoader: {
position: 'absolute',
@@ -3945,7 +4025,7 @@ const styles = (theme: ThemeColors) =>
left: 16,
bottom: 16,
width: variables.cardNameWidth,
- color: theme.text,
+ color: theme.textLight,
fontSize: variables.fontSizeSmall,
lineHeight: variables.lineHeightLarge,
},
@@ -3998,4 +4078,5 @@ type ThemeStyles = ReturnType;
const defaultStyles = styles(defaultTheme);
export default styles;
-export {defaultStyles, type Styles, type ThemeStyles, type StatusBarStyle, type ColorScheme};
+export {defaultStyles};
+export type {Styles, ThemeStyles, StatusBarStyle, ColorScheme};
diff --git a/src/styles/stylePropTypes.js b/src/styles/stylePropTypes.js
index b97decba621c..f9ecdb98ff13 100644
--- a/src/styles/stylePropTypes.js
+++ b/src/styles/stylePropTypes.js
@@ -1,3 +1,5 @@
import PropTypes from 'prop-types';
-export default PropTypes.oneOfType([PropTypes.object, PropTypes.arrayOf(PropTypes.object), PropTypes.func]);
+const stylePropTypes = PropTypes.oneOfType([PropTypes.object, PropTypes.arrayOf(PropTypes.object), PropTypes.func]);
+
+export default stylePropTypes;
diff --git a/src/styles/colors.ts b/src/styles/theme/colors.ts
similarity index 98%
rename from src/styles/colors.ts
rename to src/styles/theme/colors.ts
index 35d93f060c86..061336d2d114 100644
--- a/src/styles/colors.ts
+++ b/src/styles/theme/colors.ts
@@ -1,4 +1,4 @@
-import {Color} from './themes/types';
+import {Color} from './types';
/**
* DO NOT import colors.js into files. Use the theme switching hooks and HOCs instead.
diff --git a/src/styles/themes/ThemeContext.ts b/src/styles/theme/context/ThemeContext.ts
similarity index 59%
rename from src/styles/themes/ThemeContext.ts
rename to src/styles/theme/context/ThemeContext.ts
index ec35675953fe..64cd805b3f59 100644
--- a/src/styles/themes/ThemeContext.ts
+++ b/src/styles/theme/context/ThemeContext.ts
@@ -1,6 +1,6 @@
import React from 'react';
-import {defaultTheme} from './themes';
-import {type ThemeColors} from './types';
+import {type ThemeColors} from '@styles/theme/types';
+import {defaultTheme} from '..';
const ThemeContext = React.createContext(defaultTheme);
diff --git a/src/styles/theme/context/ThemeIllustrationsContext.ts b/src/styles/theme/context/ThemeIllustrationsContext.ts
new file mode 100644
index 000000000000..b70cf6aa5ea2
--- /dev/null
+++ b/src/styles/theme/context/ThemeIllustrationsContext.ts
@@ -0,0 +1,7 @@
+import React from 'react';
+import {DefaultIllustrations} from '@styles/theme/illustrations';
+import {IllustrationsType} from '@styles/theme/illustrations/types';
+
+const ThemeIllustrationsContext = React.createContext(DefaultIllustrations);
+
+export default ThemeIllustrationsContext;
diff --git a/src/styles/ThemeStylesContext.ts b/src/styles/theme/context/ThemeStylesContext.ts
similarity index 62%
rename from src/styles/ThemeStylesContext.ts
rename to src/styles/theme/context/ThemeStylesContext.ts
index 73f044f2dcbd..a1c81bbd9c7c 100644
--- a/src/styles/ThemeStylesContext.ts
+++ b/src/styles/theme/context/ThemeStylesContext.ts
@@ -1,8 +1,8 @@
import React from 'react';
-import {defaultStyles} from './styles';
-import type {ThemeStyles} from './styles';
-import {DefaultStyleUtils} from './utils';
-import type {StyleUtilsType} from './utils';
+import {defaultStyles} from '@styles/index';
+import type {ThemeStyles} from '@styles/index';
+import {DefaultStyleUtils} from '@styles/utils';
+import type {StyleUtilsType} from '@styles/utils';
type ThemeStylesContextType = {
styles: ThemeStyles;
diff --git a/src/styles/theme/illustrations/index.ts b/src/styles/theme/illustrations/index.ts
new file mode 100644
index 000000000000..9526dbcd1023
--- /dev/null
+++ b/src/styles/theme/illustrations/index.ts
@@ -0,0 +1,15 @@
+import {ThemePreferenceWithoutSystem} from '@styles/theme/types';
+import CONST from '@src/CONST';
+import darkIllustrations from './themes/dark';
+import lightIllustrations from './themes/light';
+import type {IllustrationsType} from './types';
+
+const Illustrations = {
+ [CONST.THEME.LIGHT]: lightIllustrations,
+ [CONST.THEME.DARK]: darkIllustrations,
+} satisfies Record;
+
+const DefaultIllustrations = Illustrations[CONST.THEME.FALLBACK];
+
+export default Illustrations;
+export {DefaultIllustrations};
diff --git a/src/styles/illustrations/dark.ts b/src/styles/theme/illustrations/themes/dark.ts
similarity index 80%
rename from src/styles/illustrations/dark.ts
rename to src/styles/theme/illustrations/themes/dark.ts
index ca52a24a4e4e..2a4390ae2520 100644
--- a/src/styles/illustrations/dark.ts
+++ b/src/styles/theme/illustrations/themes/dark.ts
@@ -1,12 +1,12 @@
import EmptyStateBackgroundImage from '@assets/images/themeDependent/empty-state_background-fade-dark.png';
import ExampleCheckEN from '@assets/images/themeDependent/example-check-image-dark-en.png';
import ExampleCheckES from '@assets/images/themeDependent/example-check-image-dark-es.png';
-import {Illustrations} from './types';
+import {IllustrationsType} from '@styles/theme/illustrations/types';
const illustrations = {
EmptyStateBackgroundImage,
ExampleCheckEN,
ExampleCheckES,
-} satisfies Illustrations;
+} satisfies IllustrationsType;
export default illustrations;
diff --git a/src/styles/illustrations/light.ts b/src/styles/theme/illustrations/themes/light.ts
similarity index 80%
rename from src/styles/illustrations/light.ts
rename to src/styles/theme/illustrations/themes/light.ts
index a953a312327f..f41114a04efa 100644
--- a/src/styles/illustrations/light.ts
+++ b/src/styles/theme/illustrations/themes/light.ts
@@ -1,12 +1,12 @@
import EmptyStateBackgroundImage from '@assets/images/themeDependent/empty-state_background-fade-light.png';
import ExampleCheckEN from '@assets/images/themeDependent/example-check-image-light-en.png';
import ExampleCheckES from '@assets/images/themeDependent/example-check-image-light-es.png';
-import {Illustrations} from './types';
+import {IllustrationsType} from '@styles/theme/illustrations/types';
const illustrations = {
EmptyStateBackgroundImage,
ExampleCheckEN,
ExampleCheckES,
-} satisfies Illustrations;
+} satisfies IllustrationsType;
export default illustrations;
diff --git a/src/styles/illustrations/types.ts b/src/styles/theme/illustrations/types.ts
similarity index 80%
rename from src/styles/illustrations/types.ts
rename to src/styles/theme/illustrations/types.ts
index bfb1db4a19c1..bda98dc55fbf 100644
--- a/src/styles/illustrations/types.ts
+++ b/src/styles/theme/illustrations/types.ts
@@ -1,10 +1,10 @@
import {ImageSourcePropType} from 'react-native';
-type Illustrations = {
+type IllustrationsType = {
EmptyStateBackgroundImage: ImageSourcePropType;
ExampleCheckES: ImageSourcePropType;
ExampleCheckEN: ImageSourcePropType;
};
// eslint-disable-next-line import/prefer-default-export
-export {type Illustrations};
+export {type IllustrationsType};
diff --git a/src/styles/themes/themes.ts b/src/styles/theme/index.ts
similarity index 69%
rename from src/styles/themes/themes.ts
rename to src/styles/theme/index.ts
index c0a305df294f..c8c3a6ec12c1 100644
--- a/src/styles/themes/themes.ts
+++ b/src/styles/theme/index.ts
@@ -1,6 +1,6 @@
import CONST from '@src/CONST';
-import darkTheme from './default';
-import lightTheme from './light';
+import darkTheme from './themes/dark';
+import lightTheme from './themes/light';
import {type ThemeColors, ThemePreferenceWithoutSystem} from './types';
const themes = {
@@ -8,7 +8,7 @@ const themes = {
[CONST.THEME.DARK]: darkTheme,
} satisfies Record;
-const defaultTheme = themes[CONST.THEME.DEFAULT];
+const defaultTheme = themes[CONST.THEME.FALLBACK];
export default themes;
export {defaultTheme};
diff --git a/src/styles/themes/default.ts b/src/styles/theme/themes/dark.ts
similarity index 96%
rename from src/styles/themes/default.ts
rename to src/styles/theme/themes/dark.ts
index d46e94da87dd..95cf518921a9 100644
--- a/src/styles/themes/default.ts
+++ b/src/styles/theme/themes/dark.ts
@@ -1,7 +1,7 @@
-import colors from '@styles/colors';
+import colors from '@styles/theme/colors';
+import {type ThemeColors} from '@styles/theme/types';
import CONST from '@src/CONST';
import SCREENS from '@src/SCREENS';
-import {type ThemeColors} from './types';
const darkTheme = {
// Figma keys
@@ -65,7 +65,6 @@ const darkTheme = {
dropUIBG: 'rgba(6,27,9,0.92)',
receiptDropUIBG: 'rgba(3, 212, 124, 0.84)',
checkBox: colors.green400,
- pickerOptionsTextColor: colors.productDark900,
imageCropBackgroundColor: colors.productDark700,
fallbackIconColor: colors.green700,
reactionActiveBackground: colors.green600,
@@ -120,7 +119,7 @@ const darkTheme = {
statusBarStyle: CONST.STATUS_BAR_STYLE.DARK_CONTENT,
},
[SCREENS.SETTINGS.PROFILE.STATUS]: {
- backgroundColor: colors.green700,
+ backgroundColor: colors.productDark100,
statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT,
},
[SCREENS.SETTINGS.ROOT]: {
diff --git a/src/styles/themes/light.ts b/src/styles/theme/themes/light.ts
similarity index 93%
rename from src/styles/themes/light.ts
rename to src/styles/theme/themes/light.ts
index 3c37ec4534d0..f10885ae4d2e 100644
--- a/src/styles/themes/light.ts
+++ b/src/styles/theme/themes/light.ts
@@ -1,7 +1,7 @@
-import colors from '@styles/colors';
+import colors from '@styles/theme/colors';
+import {type ThemeColors} from '@styles/theme/types';
import CONST from '@src/CONST';
import SCREENS from '@src/SCREENS';
-import {type ThemeColors} from './types';
const lightTheme = {
// Figma keys
@@ -65,7 +65,6 @@ const lightTheme = {
dropUIBG: 'rgba(252, 251, 249, 0.92)',
receiptDropUIBG: 'rgba(3, 212, 124, 0.84)',
checkBox: colors.green400,
- pickerOptionsTextColor: colors.productLight900,
imageCropBackgroundColor: colors.productLight700,
fallbackIconColor: colors.green700,
reactionActiveBackground: colors.green100,
@@ -112,16 +111,16 @@ const lightTheme = {
statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT,
},
[SCREENS.SETTINGS.WALLET.ROOT]: {
- backgroundColor: colors.productDark100,
- statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT,
+ backgroundColor: colors.productLight100,
+ statusBarStyle: CONST.STATUS_BAR_STYLE.DARK_CONTENT,
},
[SCREENS.SETTINGS.SECURITY]: {
backgroundColor: colors.ice500,
statusBarStyle: CONST.STATUS_BAR_STYLE.DARK_CONTENT,
},
[SCREENS.SETTINGS.PROFILE.STATUS]: {
- backgroundColor: colors.green700,
- statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT,
+ backgroundColor: colors.productLight100,
+ statusBarStyle: CONST.STATUS_BAR_STYLE.DARK_CONTENT,
},
[SCREENS.SETTINGS.ROOT]: {
backgroundColor: colors.productLight200,
diff --git a/src/styles/themes/types.ts b/src/styles/theme/types.ts
similarity index 93%
rename from src/styles/themes/types.ts
rename to src/styles/theme/types.ts
index 3a123419c384..b443295b8167 100644
--- a/src/styles/themes/types.ts
+++ b/src/styles/theme/types.ts
@@ -1,10 +1,10 @@
-import {type ColorScheme, type StatusBarStyle} from '@styles/styles';
import CONST from '@src/CONST';
+import {type ColorScheme, type StatusBarStyle} from '..';
type Color = string;
type ThemePreference = (typeof CONST.THEME)[keyof typeof CONST.THEME];
-type ThemePreferenceWithoutSystem = Exclude;
+type ThemePreferenceWithoutSystem = Exclude;
type ThemeColors = {
// Figma keys
@@ -68,7 +68,6 @@ type ThemeColors = {
dropUIBG: Color;
receiptDropUIBG: Color;
checkBox: Color;
- pickerOptionsTextColor: Color;
imageCropBackgroundColor: Color;
fallbackIconColor: Color;
reactionActiveBackground: Color;
diff --git a/src/styles/addOutlineWidth/index.native.ts b/src/styles/utils/addOutlineWidth/index.native.ts
similarity index 100%
rename from src/styles/addOutlineWidth/index.native.ts
rename to src/styles/utils/addOutlineWidth/index.native.ts
diff --git a/src/styles/addOutlineWidth/index.ts b/src/styles/utils/addOutlineWidth/index.ts
similarity index 100%
rename from src/styles/addOutlineWidth/index.ts
rename to src/styles/utils/addOutlineWidth/index.ts
diff --git a/src/styles/addOutlineWidth/types.ts b/src/styles/utils/addOutlineWidth/types.ts
similarity index 76%
rename from src/styles/addOutlineWidth/types.ts
rename to src/styles/utils/addOutlineWidth/types.ts
index 91e3f7409259..f6b9be63893c 100644
--- a/src/styles/addOutlineWidth/types.ts
+++ b/src/styles/utils/addOutlineWidth/types.ts
@@ -1,5 +1,5 @@
import {TextStyle} from 'react-native';
-import {type ThemeColors} from '@styles/themes/types';
+import {type ThemeColors} from '@styles/theme/types';
type AddOutlineWidth = (theme: ThemeColors, obj: TextStyle, val?: number, hasError?: boolean) => TextStyle;
diff --git a/src/styles/utilities/borders.ts b/src/styles/utils/borders.ts
similarity index 100%
rename from src/styles/utilities/borders.ts
rename to src/styles/utils/borders.ts
diff --git a/src/styles/cardStyles/index.native.ts b/src/styles/utils/cardStyles/index.native.ts
similarity index 100%
rename from src/styles/cardStyles/index.native.ts
rename to src/styles/utils/cardStyles/index.native.ts
diff --git a/src/styles/cardStyles/index.ts b/src/styles/utils/cardStyles/index.ts
similarity index 81%
rename from src/styles/cardStyles/index.ts
rename to src/styles/utils/cardStyles/index.ts
index b5d3c069b73a..77a00695d20a 100644
--- a/src/styles/cardStyles/index.ts
+++ b/src/styles/utils/cardStyles/index.ts
@@ -1,4 +1,4 @@
-import positioning from '@styles/utilities/positioning';
+import positioning from '@styles/utils/positioning';
import GetCardStyles from './types';
/**
diff --git a/src/styles/cardStyles/types.ts b/src/styles/utils/cardStyles/types.ts
similarity index 100%
rename from src/styles/cardStyles/types.ts
rename to src/styles/utils/cardStyles/types.ts
diff --git a/src/styles/codeStyles/index.android.ts b/src/styles/utils/codeStyles/index.android.ts
similarity index 100%
rename from src/styles/codeStyles/index.android.ts
rename to src/styles/utils/codeStyles/index.android.ts
diff --git a/src/styles/codeStyles/index.ios.ts b/src/styles/utils/codeStyles/index.ios.ts
similarity index 100%
rename from src/styles/codeStyles/index.ios.ts
rename to src/styles/utils/codeStyles/index.ios.ts
diff --git a/src/styles/codeStyles/index.ts b/src/styles/utils/codeStyles/index.ts
similarity index 100%
rename from src/styles/codeStyles/index.ts
rename to src/styles/utils/codeStyles/index.ts
diff --git a/src/styles/codeStyles/types.ts b/src/styles/utils/codeStyles/types.ts
similarity index 100%
rename from src/styles/codeStyles/types.ts
rename to src/styles/utils/codeStyles/types.ts
diff --git a/src/styles/containerComposeStyles/index.native.ts b/src/styles/utils/containerComposeStyles/index.native.ts
similarity index 100%
rename from src/styles/containerComposeStyles/index.native.ts
rename to src/styles/utils/containerComposeStyles/index.native.ts
diff --git a/src/styles/containerComposeStyles/index.ts b/src/styles/utils/containerComposeStyles/index.ts
similarity index 100%
rename from src/styles/containerComposeStyles/index.ts
rename to src/styles/utils/containerComposeStyles/index.ts
diff --git a/src/styles/containerComposeStyles/types.ts b/src/styles/utils/containerComposeStyles/types.ts
similarity index 75%
rename from src/styles/containerComposeStyles/types.ts
rename to src/styles/utils/containerComposeStyles/types.ts
index 9930e0230016..2b2d23b7f8c1 100644
--- a/src/styles/containerComposeStyles/types.ts
+++ b/src/styles/utils/containerComposeStyles/types.ts
@@ -1,5 +1,5 @@
import {ViewStyle} from 'react-native';
-import {type ThemeStyles} from '@styles/styles';
+import {type ThemeStyles} from '@styles/index';
type ContainerComposeStyles = (styles: ThemeStyles) => ViewStyle[];
diff --git a/src/styles/utilities/cursor/index.native.ts b/src/styles/utils/cursor/index.native.ts
similarity index 100%
rename from src/styles/utilities/cursor/index.native.ts
rename to src/styles/utils/cursor/index.native.ts
diff --git a/src/styles/utilities/cursor/index.ts b/src/styles/utils/cursor/index.ts
similarity index 100%
rename from src/styles/utilities/cursor/index.ts
rename to src/styles/utils/cursor/index.ts
diff --git a/src/styles/utilities/cursor/types.ts b/src/styles/utils/cursor/types.ts
similarity index 100%
rename from src/styles/utilities/cursor/types.ts
rename to src/styles/utils/cursor/types.ts
diff --git a/src/styles/utilities/display.ts b/src/styles/utils/display.ts
similarity index 100%
rename from src/styles/utilities/display.ts
rename to src/styles/utils/display.ts
diff --git a/src/styles/editedLabelStyles/index.native.ts b/src/styles/utils/editedLabelStyles/index.native.ts
similarity index 100%
rename from src/styles/editedLabelStyles/index.native.ts
rename to src/styles/utils/editedLabelStyles/index.native.ts
diff --git a/src/styles/editedLabelStyles/index.ts b/src/styles/utils/editedLabelStyles/index.ts
similarity index 67%
rename from src/styles/editedLabelStyles/index.ts
rename to src/styles/utils/editedLabelStyles/index.ts
index 339c50d8a4df..427918af1202 100644
--- a/src/styles/editedLabelStyles/index.ts
+++ b/src/styles/utils/editedLabelStyles/index.ts
@@ -1,5 +1,5 @@
-import display from '@styles/utilities/display';
-import flex from '@styles/utilities/flex';
+import display from '@styles/utils/display';
+import flex from '@styles/utils/flex';
import EditedLabelStyles from './types';
const editedLabelStyles: EditedLabelStyles = {
diff --git a/src/styles/editedLabelStyles/types.ts b/src/styles/utils/editedLabelStyles/types.ts
similarity index 100%
rename from src/styles/editedLabelStyles/types.ts
rename to src/styles/utils/editedLabelStyles/types.ts
diff --git a/src/styles/utilities/flex.ts b/src/styles/utils/flex.ts
similarity index 100%
rename from src/styles/utilities/flex.ts
rename to src/styles/utils/flex.ts
diff --git a/src/styles/fontFamily/bold/index.android.ts b/src/styles/utils/fontFamily/bold/index.android.ts
similarity index 100%
rename from src/styles/fontFamily/bold/index.android.ts
rename to src/styles/utils/fontFamily/bold/index.android.ts
diff --git a/src/styles/fontFamily/bold/index.ios.ts b/src/styles/utils/fontFamily/bold/index.ios.ts
similarity index 100%
rename from src/styles/fontFamily/bold/index.ios.ts
rename to src/styles/utils/fontFamily/bold/index.ios.ts
diff --git a/src/styles/fontFamily/bold/index.ts b/src/styles/utils/fontFamily/bold/index.ts
similarity index 100%
rename from src/styles/fontFamily/bold/index.ts
rename to src/styles/utils/fontFamily/bold/index.ts
diff --git a/src/styles/fontFamily/bold/types.ts b/src/styles/utils/fontFamily/bold/types.ts
similarity index 100%
rename from src/styles/fontFamily/bold/types.ts
rename to src/styles/utils/fontFamily/bold/types.ts
diff --git a/src/styles/fontFamily/index.native.ts b/src/styles/utils/fontFamily/index.native.ts
similarity index 100%
rename from src/styles/fontFamily/index.native.ts
rename to src/styles/utils/fontFamily/index.native.ts
diff --git a/src/styles/fontFamily/index.ts b/src/styles/utils/fontFamily/index.ts
similarity index 100%
rename from src/styles/fontFamily/index.ts
rename to src/styles/utils/fontFamily/index.ts
diff --git a/src/styles/fontFamily/multiFontFamily.ts b/src/styles/utils/fontFamily/multiFontFamily.ts
similarity index 100%
rename from src/styles/fontFamily/multiFontFamily.ts
rename to src/styles/utils/fontFamily/multiFontFamily.ts
diff --git a/src/styles/fontFamily/singleFontFamily.ts b/src/styles/utils/fontFamily/singleFontFamily.ts
similarity index 100%
rename from src/styles/fontFamily/singleFontFamily.ts
rename to src/styles/utils/fontFamily/singleFontFamily.ts
diff --git a/src/styles/fontFamily/types.ts b/src/styles/utils/fontFamily/types.ts
similarity index 100%
rename from src/styles/fontFamily/types.ts
rename to src/styles/utils/fontFamily/types.ts
diff --git a/src/styles/fontWeight/bold/index.android.ts b/src/styles/utils/fontWeight/bold/index.android.ts
similarity index 100%
rename from src/styles/fontWeight/bold/index.android.ts
rename to src/styles/utils/fontWeight/bold/index.android.ts
diff --git a/src/styles/fontWeight/bold/index.ts b/src/styles/utils/fontWeight/bold/index.ts
similarity index 100%
rename from src/styles/fontWeight/bold/index.ts
rename to src/styles/utils/fontWeight/bold/index.ts
diff --git a/src/styles/fontWeight/bold/types.ts b/src/styles/utils/fontWeight/bold/types.ts
similarity index 100%
rename from src/styles/fontWeight/bold/types.ts
rename to src/styles/utils/fontWeight/bold/types.ts
diff --git a/src/styles/utils/ModalStyleUtils.ts b/src/styles/utils/generators/ModalStyleUtils.ts
similarity index 95%
rename from src/styles/utils/ModalStyleUtils.ts
rename to src/styles/utils/generators/ModalStyleUtils.ts
index cf312a4e76f7..67faec504d61 100644
--- a/src/styles/utils/ModalStyleUtils.ts
+++ b/src/styles/utils/generators/ModalStyleUtils.ts
@@ -1,10 +1,10 @@
import {ViewStyle} from 'react-native';
import {ModalProps} from 'react-native-modal';
import {ValueOf} from 'type-fest';
-import {type ThemeStyles} from '@styles/styles';
-import {type ThemeColors} from '@styles/themes/types';
+import {ThemeStyles} from '@styles/index';
import variables from '@styles/variables';
import CONST from '@src/CONST';
+import StyleUtilGenerator from './types';
function getCenteredModalStyles(styles: ThemeStyles, windowWidth: number, isSmallScreenWidth: boolean, isFullScreenWhenSmall = false): ViewStyle {
const modalStyles = styles.centeredModalStyles(isSmallScreenWidth, isFullScreenWhenSmall);
@@ -36,14 +36,18 @@ type GetModalStyles = {
shouldAddTopSafeAreaPadding: boolean;
};
-const createModalStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({
+type GetModalStylesStyleUtil = {
getModalStyles: (
type: ModalType | undefined,
windowDimensions: WindowDimensions,
- popoverAnchorPosition: ViewStyle = {},
- innerContainerStyle: ViewStyle = {},
- outerStyle: ViewStyle = {},
- ): GetModalStyles => {
+ popoverAnchorPosition?: ViewStyle,
+ innerContainerStyle?: ViewStyle,
+ outerStyle?: ViewStyle,
+ ) => GetModalStyles;
+};
+
+const createModalStyleUtils: StyleUtilGenerator = ({theme, styles}) => ({
+ getModalStyles: (type, windowDimensions, popoverAnchorPosition = {}, innerContainerStyle = {}, outerStyle = {}): GetModalStyles => {
const {isSmallScreenWidth, windowWidth} = windowDimensions;
let modalStyle: GetModalStyles['modalStyle'] = {
diff --git a/src/styles/utils/ReportActionContextMenuStyleUtils.ts b/src/styles/utils/generators/ReportActionContextMenuStyleUtils.ts
similarity index 73%
rename from src/styles/utils/ReportActionContextMenuStyleUtils.ts
rename to src/styles/utils/generators/ReportActionContextMenuStyleUtils.ts
index 9dd3c2a0d203..10904fca05cd 100644
--- a/src/styles/utils/ReportActionContextMenuStyleUtils.ts
+++ b/src/styles/utils/generators/ReportActionContextMenuStyleUtils.ts
@@ -1,7 +1,8 @@
import {ViewStyle} from 'react-native';
-import {type ThemeStyles} from '@styles/styles';
-import {type ThemeColors} from '@styles/themes/types';
+import {ThemeStyles} from '@styles/index';
+import {type ThemeColors} from '@styles/theme/types';
import variables from '@styles/variables';
+import StyleUtilGenerator from './types';
const getDefaultWrapperStyle = (theme: ThemeColors): ViewStyle => ({
backgroundColor: theme.componentBG,
@@ -20,6 +21,8 @@ const getMiniWrapperStyle = (theme: ThemeColors, styles: ThemeStyles): ViewStyle
},
];
+type GetReportActionContextMenuStylesStyleUtil = {getReportActionContextMenuStyles: (isMini: boolean, isSmallScreenWidth: boolean) => ViewStyle[]};
+
/**
* Generate the wrapper styles for the ReportActionContextMenu.
*
@@ -27,8 +30,8 @@ const getMiniWrapperStyle = (theme: ThemeColors, styles: ThemeStyles): ViewStyle
* @param isSmallScreenWidth
* @param theme
*/
-const createReportActionContextMenuStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({
- getReportActionContextMenuStyles: (isMini: boolean, isSmallScreenWidth: boolean): ViewStyle[] => {
+const createReportActionContextMenuStyleUtils: StyleUtilGenerator = ({theme, styles}) => ({
+ getReportActionContextMenuStyles: (isMini, isSmallScreenWidth) => {
if (isMini) {
return getMiniWrapperStyle(theme, styles);
}
diff --git a/src/styles/utils/TooltipStyleUtils.ts b/src/styles/utils/generators/TooltipStyleUtils.ts
similarity index 96%
rename from src/styles/utils/TooltipStyleUtils.ts
rename to src/styles/utils/generators/TooltipStyleUtils.ts
index 5c12a27ee981..0e9fda1a3388 100644
--- a/src/styles/utils/TooltipStyleUtils.ts
+++ b/src/styles/utils/generators/TooltipStyleUtils.ts
@@ -1,11 +1,10 @@
import {Animated, TextStyle, View, ViewStyle} from 'react-native';
-import fontFamily from '@styles/fontFamily';
-import {type ThemeStyles} from '@styles/styles';
-import {type ThemeColors} from '@styles/themes/types';
-import positioning from '@styles/utilities/positioning';
-import spacing from '@styles/utilities/spacing';
+import fontFamily from '@styles/utils/fontFamily';
+import positioning from '@styles/utils/positioning';
+import roundToNearestMultipleOfFour from '@styles/utils/roundToNearestMultipleOfFour';
+import spacing from '@styles/utils/spacing';
import variables from '@styles/variables';
-import roundToNearestMultipleOfFour from './roundToNearestMultipleOfFour';
+import StyleUtilGenerator from './types';
/** This defines the proximity with the edge of the window in which tooltips should not be displayed.
* If a tooltip is too close to the edge of the screen, we'll shift it towards the center. */
@@ -116,6 +115,8 @@ type TooltipParams = {
manualShiftVertical?: number;
};
+type GetTooltipStylesStyleUtil = {getTooltipStyles: (props: TooltipParams) => TooltipStyles};
+
/**
* Generate styles for the tooltip component.
*
@@ -137,7 +138,7 @@ type TooltipParams = {
* @param [manualShiftVertical] - Any additional amount to manually shift the tooltip up or down.
* A positive value shifts it down, and a negative value shifts it up.
*/
-const createTooltipStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({
+const createTooltipStyleUtils: StyleUtilGenerator = ({theme, styles}) => ({
getTooltipStyles: ({
tooltip,
currentSize,
@@ -151,7 +152,7 @@ const createTooltipStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({
tooltipWrapperHeight,
manualShiftHorizontal = 0,
manualShiftVertical = 0,
- }: TooltipParams): TooltipStyles => {
+ }) => {
const tooltipVerticalPadding = spacing.pv1;
// We calculate tooltip width based on the tooltip's content width
diff --git a/src/styles/utils/generators/types.ts b/src/styles/utils/generators/types.ts
new file mode 100644
index 000000000000..ca6a630d94c5
--- /dev/null
+++ b/src/styles/utils/generators/types.ts
@@ -0,0 +1,6 @@
+import {ThemeStyles} from '@styles/index';
+import {ThemeColors} from '@styles/theme/types';
+
+type StyleUtilGenerator> = (props: {theme: ThemeColors; styles: ThemeStyles}) => StyleUtil;
+
+export default StyleUtilGenerator;
diff --git a/src/styles/getContextMenuItemStyles/index.native.ts b/src/styles/utils/getContextMenuItemStyles/index.native.ts
similarity index 100%
rename from src/styles/getContextMenuItemStyles/index.native.ts
rename to src/styles/utils/getContextMenuItemStyles/index.native.ts
diff --git a/src/styles/getContextMenuItemStyles/index.ts b/src/styles/utils/getContextMenuItemStyles/index.ts
similarity index 100%
rename from src/styles/getContextMenuItemStyles/index.ts
rename to src/styles/utils/getContextMenuItemStyles/index.ts
diff --git a/src/styles/getContextMenuItemStyles/types.ts b/src/styles/utils/getContextMenuItemStyles/types.ts
similarity index 77%
rename from src/styles/getContextMenuItemStyles/types.ts
rename to src/styles/utils/getContextMenuItemStyles/types.ts
index 376fb8d46d7c..06b989becf8a 100644
--- a/src/styles/getContextMenuItemStyles/types.ts
+++ b/src/styles/utils/getContextMenuItemStyles/types.ts
@@ -1,5 +1,5 @@
import {ViewStyle} from 'react-native';
-import {type ThemeStyles} from '@styles/styles';
+import {type ThemeStyles} from '@styles/index';
type GetContextMenuItemStyle = (styles: ThemeStyles, windowWidth?: number) => ViewStyle[];
diff --git a/src/styles/getNavigationModalCardStyles/index.desktop.ts b/src/styles/utils/getNavigationModalCardStyles/index.desktop.ts
similarity index 90%
rename from src/styles/getNavigationModalCardStyles/index.desktop.ts
rename to src/styles/utils/getNavigationModalCardStyles/index.desktop.ts
index 9e182636ad76..523f82048a02 100644
--- a/src/styles/getNavigationModalCardStyles/index.desktop.ts
+++ b/src/styles/utils/getNavigationModalCardStyles/index.desktop.ts
@@ -1,4 +1,4 @@
-import positioning from '@styles/utilities/positioning';
+import positioning from '@styles/utils/positioning';
import GetNavigationModalCardStyles from './types';
const getNavigationModalCardStyles: GetNavigationModalCardStyles = () => ({
diff --git a/src/styles/getNavigationModalCardStyles/index.ts b/src/styles/utils/getNavigationModalCardStyles/index.ts
similarity index 100%
rename from src/styles/getNavigationModalCardStyles/index.ts
rename to src/styles/utils/getNavigationModalCardStyles/index.ts
diff --git a/src/styles/getNavigationModalCardStyles/index.website.ts b/src/styles/utils/getNavigationModalCardStyles/index.website.ts
similarity index 90%
rename from src/styles/getNavigationModalCardStyles/index.website.ts
rename to src/styles/utils/getNavigationModalCardStyles/index.website.ts
index 9e182636ad76..523f82048a02 100644
--- a/src/styles/getNavigationModalCardStyles/index.website.ts
+++ b/src/styles/utils/getNavigationModalCardStyles/index.website.ts
@@ -1,4 +1,4 @@
-import positioning from '@styles/utilities/positioning';
+import positioning from '@styles/utils/positioning';
import GetNavigationModalCardStyles from './types';
const getNavigationModalCardStyles: GetNavigationModalCardStyles = () => ({
diff --git a/src/styles/getNavigationModalCardStyles/types.ts b/src/styles/utils/getNavigationModalCardStyles/types.ts
similarity index 100%
rename from src/styles/getNavigationModalCardStyles/types.ts
rename to src/styles/utils/getNavigationModalCardStyles/types.ts
diff --git a/src/styles/getPopOverVerticalOffset/index.desktop.ts b/src/styles/utils/getPopOverVerticalOffset/index.desktop.ts
similarity index 100%
rename from src/styles/getPopOverVerticalOffset/index.desktop.ts
rename to src/styles/utils/getPopOverVerticalOffset/index.desktop.ts
diff --git a/src/styles/getPopOverVerticalOffset/index.ts b/src/styles/utils/getPopOverVerticalOffset/index.ts
similarity index 100%
rename from src/styles/getPopOverVerticalOffset/index.ts
rename to src/styles/utils/getPopOverVerticalOffset/index.ts
diff --git a/src/styles/getPopOverVerticalOffset/types.ts b/src/styles/utils/getPopOverVerticalOffset/types.ts
similarity index 100%
rename from src/styles/getPopOverVerticalOffset/types.ts
rename to src/styles/utils/getPopOverVerticalOffset/types.ts
diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts
index 8d52c8de200a..a48724e72fc9 100644
--- a/src/styles/utils/index.ts
+++ b/src/styles/utils/index.ts
@@ -1,65 +1,38 @@
import {CSSProperties} from 'react';
-import {Animated, DimensionValue, ImageStyle, PressableStateCallbackType, StyleProp, TextStyle, ViewStyle} from 'react-native';
+import {Animated, DimensionValue, PressableStateCallbackType, StyleProp, StyleSheet, TextStyle, ViewStyle} from 'react-native';
import {EdgeInsets} from 'react-native-safe-area-context';
import {ValueOf} from 'type-fest';
import * as Browser from '@libs/Browser';
import * as UserUtils from '@libs/UserUtils';
-import colors from '@styles/colors';
-import containerComposeStyles from '@styles/containerComposeStyles';
-import fontFamily from '@styles/fontFamily';
-import getContextMenuItemStyles from '@styles/getContextMenuItemStyles';
-import {compactContentContainerStyles} from '@styles/optionRowStyles';
-import {defaultStyles, type ThemeStyles} from '@styles/styles';
-import {defaultTheme} from '@styles/themes/themes';
-import {ThemeColors} from '@styles/themes/types';
-import cursor from '@styles/utilities/cursor';
-import positioning from '@styles/utilities/positioning';
-import spacing from '@styles/utilities/spacing';
+import {defaultTheme} from '@styles/theme';
+import colors from '@styles/theme/colors';
+import {ThemeColors} from '@styles/theme/types';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import {Transaction} from '@src/types/onyx';
-import createModalStyleUtils from './ModalStyleUtils';
-import createReportActionContextMenuStyleUtils from './ReportActionContextMenuStyleUtils';
-import createTooltipStyleUtils from './TooltipStyleUtils';
-
-type AllStyles = ViewStyle | TextStyle | ImageStyle;
-type ParsableStyle = StyleProp | ((state: PressableStateCallbackType) => StyleProp);
-
-type ColorValue = ValueOf;
-type AvatarSizeName = ValueOf;
-type EReceiptColorName = ValueOf;
-type AvatarSizeValue = ValueOf<
- Pick<
- typeof variables,
- | 'avatarSizeNormal'
- | 'avatarSizeSmallSubscript'
- | 'avatarSizeMidSubscript'
- | 'avatarSizeSubscript'
- | 'avatarSizeSmall'
- | 'avatarSizeSmaller'
- | 'avatarSizeXLarge'
- | 'avatarSizeLarge'
- | 'avatarSizeMedium'
- | 'avatarSizeLargeBordered'
- | 'avatarSizeHeader'
- | 'avatarSizeMentionIcon'
- | 'avatarSizeSmallNormal'
- >
->;
-
-type AvatarStyle = {
- width: number;
- height: number;
- borderRadius: number;
- backgroundColor: string;
-};
-
-type ButtonSizeValue = ValueOf;
-type ButtonStateName = ValueOf;
-type AvatarSize = {width: number};
-
-type WorkspaceColorStyle = {backgroundColor: ColorValue; fill: ColorValue};
-type EreceiptColorStyle = {backgroundColor: ColorValue; color: ColorValue};
+import {defaultStyles, type ThemeStyles} from '..';
+import containerComposeStyles from './containerComposeStyles';
+import fontFamily from './fontFamily';
+import createModalStyleUtils from './generators/ModalStyleUtils';
+import createReportActionContextMenuStyleUtils from './generators/ReportActionContextMenuStyleUtils';
+import createTooltipStyleUtils from './generators/TooltipStyleUtils';
+import getContextMenuItemStyles from './getContextMenuItemStyles';
+import {compactContentContainerStyles} from './optionRowStyles';
+import positioning from './positioning';
+import spacing from './spacing';
+import {
+ AllStyles,
+ AvatarSize,
+ AvatarSizeName,
+ AvatarSizeValue,
+ AvatarStyle,
+ ButtonSizeValue,
+ ButtonStateName,
+ EReceiptColorName,
+ EreceiptColorStyle,
+ ParsableStyle,
+ WorkspaceColorStyle,
+} from './types';
const workspaceColorOptions: WorkspaceColorStyle[] = [
{backgroundColor: colors.blue200, fill: colors.blue700},
@@ -473,13 +446,6 @@ function getBackgroundColorWithOpacityStyle(backgroundColor: string, opacity: nu
return {};
}
-function getAnimatedFABStyle(rotate: Animated.Value, backgroundColor: Animated.Value): Animated.WithAnimatedValue {
- return {
- transform: [{rotate}],
- backgroundColor,
- };
-}
-
function getWidthAndHeightStyle(width: number, height?: number): ViewStyle {
return {
width,
@@ -1042,7 +1008,6 @@ const staticStyleUtils = {
combineStyles,
displayIfTrue,
getAmountFontSizeAndLineHeight,
- getAnimatedFABStyle,
getAutoCompleteSuggestionContainerStyle,
getAvatarBorderRadius,
getAvatarBorderStyle,
@@ -1103,9 +1068,13 @@ const staticStyleUtils = {
const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({
...staticStyleUtils,
- ...createModalStyleUtils(theme, styles),
- ...createTooltipStyleUtils(theme, styles),
- ...createReportActionContextMenuStyleUtils(theme, styles),
+ ...createModalStyleUtils({theme, styles}),
+ ...createTooltipStyleUtils({theme, styles}),
+ ...createReportActionContextMenuStyleUtils({theme, styles}),
+
+ getCompactContentContainerStyles: () => compactContentContainerStyles(styles),
+ getContextMenuItemStyles: (windowWidth?: number) => getContextMenuItemStyles(styles, windowWidth),
+ getContainerComposeStyles: () => containerComposeStyles(styles),
/**
* Gets styles for AutoCompleteSuggestion row
@@ -1236,7 +1205,7 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({
getDisabledLinkStyles: (isDisabled = false): ViewStyle => {
const disabledLinkStyles = {
color: theme.textSupporting,
- ...cursor.cursorDisabled,
+ ...styles.cursorDisabled,
};
// TODO: Remove this "eslint-disable-next" once the theme switching migration is done and styles are fully typed (GH Issue: https://github.com/Expensify/App/issues/27337)
@@ -1247,6 +1216,19 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({
};
},
+ /**
+ * Get the style for the AM and PM buttons in the TimePicker
+ */
+ getStatusAMandPMButtonStyle: (amPmValue: string): {styleForAM: ViewStyle; styleForPM: ViewStyle} => {
+ const computedStyleForAM: ViewStyle = amPmValue !== CONST.TIME_PERIOD.AM ? {backgroundColor: theme.componentBG} : {};
+ const computedStyleForPM: ViewStyle = amPmValue !== CONST.TIME_PERIOD.PM ? {backgroundColor: theme.componentBG} : {};
+
+ return {
+ styleForAM: [styles.timePickerWidth100, computedStyleForAM] as unknown as ViewStyle,
+ styleForPM: [styles.timePickerWidth100, computedStyleForPM] as unknown as ViewStyle,
+ };
+ },
+
/**
* Get the styles of the text next to dot indicators
*/
@@ -1441,11 +1423,7 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({
return containerStyles;
},
- getCompactContentContainerStyles: () => compactContentContainerStyles(styles),
-
- getContextMenuItemStyles: (windowWidth?: number) => getContextMenuItemStyles(styles, windowWidth),
-
- getContainerComposeStyles: () => containerComposeStyles(styles),
+ getFullscreenCenteredContentStyles: () => [StyleSheet.absoluteFill, styles.justifyContentCenter, styles.alignItemsCenter],
});
type StyleUtilsType = ReturnType;
diff --git a/src/styles/italic/index.android.ts b/src/styles/utils/italic/index.android.ts
similarity index 100%
rename from src/styles/italic/index.android.ts
rename to src/styles/utils/italic/index.android.ts
diff --git a/src/styles/italic/index.ts b/src/styles/utils/italic/index.ts
similarity index 100%
rename from src/styles/italic/index.ts
rename to src/styles/utils/italic/index.ts
diff --git a/src/styles/italic/types.ts b/src/styles/utils/italic/types.ts
similarity index 100%
rename from src/styles/italic/types.ts
rename to src/styles/utils/italic/types.ts
diff --git a/src/styles/utilities/objectFit.ts b/src/styles/utils/objectFit.ts
similarity index 100%
rename from src/styles/utilities/objectFit.ts
rename to src/styles/utils/objectFit.ts
diff --git a/src/styles/optionAlternateTextPlatformStyles/index.ios.ts b/src/styles/utils/optionAlternateTextPlatformStyles/index.ios.ts
similarity index 100%
rename from src/styles/optionAlternateTextPlatformStyles/index.ios.ts
rename to src/styles/utils/optionAlternateTextPlatformStyles/index.ios.ts
diff --git a/src/styles/optionAlternateTextPlatformStyles/index.ts b/src/styles/utils/optionAlternateTextPlatformStyles/index.ts
similarity index 100%
rename from src/styles/optionAlternateTextPlatformStyles/index.ts
rename to src/styles/utils/optionAlternateTextPlatformStyles/index.ts
diff --git a/src/styles/optionAlternateTextPlatformStyles/types.ts b/src/styles/utils/optionAlternateTextPlatformStyles/types.ts
similarity index 100%
rename from src/styles/optionAlternateTextPlatformStyles/types.ts
rename to src/styles/utils/optionAlternateTextPlatformStyles/types.ts
diff --git a/src/styles/optionRowStyles/index.native.ts b/src/styles/utils/optionRowStyles/index.native.ts
similarity index 100%
rename from src/styles/optionRowStyles/index.native.ts
rename to src/styles/utils/optionRowStyles/index.native.ts
diff --git a/src/styles/optionRowStyles/index.ts b/src/styles/utils/optionRowStyles/index.ts
similarity index 100%
rename from src/styles/optionRowStyles/index.ts
rename to src/styles/utils/optionRowStyles/index.ts
diff --git a/src/styles/optionRowStyles/types.ts b/src/styles/utils/optionRowStyles/types.ts
similarity index 76%
rename from src/styles/optionRowStyles/types.ts
rename to src/styles/utils/optionRowStyles/types.ts
index 3425d7812be2..a31b94e0fc79 100644
--- a/src/styles/optionRowStyles/types.ts
+++ b/src/styles/utils/optionRowStyles/types.ts
@@ -1,5 +1,5 @@
import {ViewStyle} from 'react-native';
-import {type ThemeStyles} from '@styles/styles';
+import {type ThemeStyles} from '@styles/index';
type CompactContentContainerStyles = (styles: ThemeStyles) => ViewStyle;
diff --git a/src/styles/utilities/overflow.ts b/src/styles/utils/overflow.ts
similarity index 100%
rename from src/styles/utilities/overflow.ts
rename to src/styles/utils/overflow.ts
diff --git a/src/styles/utilities/overflowAuto/index.native.ts b/src/styles/utils/overflowAuto/index.native.ts
similarity index 100%
rename from src/styles/utilities/overflowAuto/index.native.ts
rename to src/styles/utils/overflowAuto/index.native.ts
diff --git a/src/styles/utilities/overflowAuto/index.ts b/src/styles/utils/overflowAuto/index.ts
similarity index 100%
rename from src/styles/utilities/overflowAuto/index.ts
rename to src/styles/utils/overflowAuto/index.ts
diff --git a/src/styles/utilities/overflowAuto/types.ts b/src/styles/utils/overflowAuto/types.ts
similarity index 100%
rename from src/styles/utilities/overflowAuto/types.ts
rename to src/styles/utils/overflowAuto/types.ts
diff --git a/src/styles/overflowXHidden/index.native.ts b/src/styles/utils/overflowXHidden/index.native.ts
similarity index 100%
rename from src/styles/overflowXHidden/index.native.ts
rename to src/styles/utils/overflowXHidden/index.native.ts
diff --git a/src/styles/overflowXHidden/index.ts b/src/styles/utils/overflowXHidden/index.ts
similarity index 100%
rename from src/styles/overflowXHidden/index.ts
rename to src/styles/utils/overflowXHidden/index.ts
diff --git a/src/styles/overflowXHidden/types.ts b/src/styles/utils/overflowXHidden/types.ts
similarity index 100%
rename from src/styles/overflowXHidden/types.ts
rename to src/styles/utils/overflowXHidden/types.ts
diff --git a/src/styles/utilities/overscrollBehaviorContain/index.native.ts b/src/styles/utils/overscrollBehaviorContain/index.native.ts
similarity index 100%
rename from src/styles/utilities/overscrollBehaviorContain/index.native.ts
rename to src/styles/utils/overscrollBehaviorContain/index.native.ts
diff --git a/src/styles/utilities/overscrollBehaviorContain/index.ts b/src/styles/utils/overscrollBehaviorContain/index.ts
similarity index 100%
rename from src/styles/utilities/overscrollBehaviorContain/index.ts
rename to src/styles/utils/overscrollBehaviorContain/index.ts
diff --git a/src/styles/utilities/overscrollBehaviorContain/types.ts b/src/styles/utils/overscrollBehaviorContain/types.ts
similarity index 100%
rename from src/styles/utilities/overscrollBehaviorContain/types.ts
rename to src/styles/utils/overscrollBehaviorContain/types.ts
diff --git a/src/styles/pointerEventsAuto/index.native.ts b/src/styles/utils/pointerEventsAuto/index.native.ts
similarity index 100%
rename from src/styles/pointerEventsAuto/index.native.ts
rename to src/styles/utils/pointerEventsAuto/index.native.ts
diff --git a/src/styles/pointerEventsAuto/index.ts b/src/styles/utils/pointerEventsAuto/index.ts
similarity index 100%
rename from src/styles/pointerEventsAuto/index.ts
rename to src/styles/utils/pointerEventsAuto/index.ts
diff --git a/src/styles/pointerEventsAuto/types.ts b/src/styles/utils/pointerEventsAuto/types.ts
similarity index 100%
rename from src/styles/pointerEventsAuto/types.ts
rename to src/styles/utils/pointerEventsAuto/types.ts
diff --git a/src/styles/pointerEventsBoxNone/index.native.ts b/src/styles/utils/pointerEventsBoxNone/index.native.ts
similarity index 100%
rename from src/styles/pointerEventsBoxNone/index.native.ts
rename to src/styles/utils/pointerEventsBoxNone/index.native.ts
diff --git a/src/styles/pointerEventsBoxNone/index.ts b/src/styles/utils/pointerEventsBoxNone/index.ts
similarity index 100%
rename from src/styles/pointerEventsBoxNone/index.ts
rename to src/styles/utils/pointerEventsBoxNone/index.ts
diff --git a/src/styles/pointerEventsBoxNone/types.ts b/src/styles/utils/pointerEventsBoxNone/types.ts
similarity index 100%
rename from src/styles/pointerEventsBoxNone/types.ts
rename to src/styles/utils/pointerEventsBoxNone/types.ts
diff --git a/src/styles/pointerEventsNone/index.native.ts b/src/styles/utils/pointerEventsNone/index.native.ts
similarity index 100%
rename from src/styles/pointerEventsNone/index.native.ts
rename to src/styles/utils/pointerEventsNone/index.native.ts
diff --git a/src/styles/pointerEventsNone/index.ts b/src/styles/utils/pointerEventsNone/index.ts
similarity index 100%
rename from src/styles/pointerEventsNone/index.ts
rename to src/styles/utils/pointerEventsNone/index.ts
diff --git a/src/styles/pointerEventsNone/types.ts b/src/styles/utils/pointerEventsNone/types.ts
similarity index 100%
rename from src/styles/pointerEventsNone/types.ts
rename to src/styles/utils/pointerEventsNone/types.ts
diff --git a/src/styles/utilities/positioning.ts b/src/styles/utils/positioning.ts
similarity index 100%
rename from src/styles/utilities/positioning.ts
rename to src/styles/utils/positioning.ts
diff --git a/src/styles/utilities/sizing.ts b/src/styles/utils/sizing.ts
similarity index 100%
rename from src/styles/utilities/sizing.ts
rename to src/styles/utils/sizing.ts
diff --git a/src/styles/utilities/spacing.ts b/src/styles/utils/spacing.ts
similarity index 99%
rename from src/styles/utilities/spacing.ts
rename to src/styles/utils/spacing.ts
index 7d568847ab65..b2597fc64603 100644
--- a/src/styles/utilities/spacing.ts
+++ b/src/styles/utils/spacing.ts
@@ -271,6 +271,10 @@ export default {
marginBottom: 40,
},
+ mb12: {
+ marginBottom: 48,
+ },
+
mb15: {
marginBottom: 60,
},
diff --git a/src/styles/utilities/textDecorationLine.ts b/src/styles/utils/textDecorationLine.ts
similarity index 100%
rename from src/styles/utilities/textDecorationLine.ts
rename to src/styles/utils/textDecorationLine.ts
diff --git a/src/styles/utilities/textUnderline/index.native.ts b/src/styles/utils/textUnderline/index.native.ts
similarity index 100%
rename from src/styles/utilities/textUnderline/index.native.ts
rename to src/styles/utils/textUnderline/index.native.ts
diff --git a/src/styles/utilities/textUnderline/index.ts b/src/styles/utils/textUnderline/index.ts
similarity index 100%
rename from src/styles/utilities/textUnderline/index.ts
rename to src/styles/utils/textUnderline/index.ts
diff --git a/src/styles/utilities/textUnderline/types.ts b/src/styles/utils/textUnderline/types.ts
similarity index 100%
rename from src/styles/utilities/textUnderline/types.ts
rename to src/styles/utils/textUnderline/types.ts
diff --git a/src/styles/utils/types.ts b/src/styles/utils/types.ts
new file mode 100644
index 000000000000..c7e1fc60a142
--- /dev/null
+++ b/src/styles/utils/types.ts
@@ -0,0 +1,59 @@
+import {ImageStyle, PressableStateCallbackType, StyleProp, TextStyle, ViewStyle} from 'react-native';
+import {ValueOf} from 'type-fest';
+import type colors from '@styles/theme/colors';
+import type variables from '@styles/variables';
+import type CONST from '@src/CONST';
+
+type AllStyles = ViewStyle | TextStyle | ImageStyle;
+type ParsableStyle = StyleProp | ((state: PressableStateCallbackType) => StyleProp);
+
+type ColorValue = ValueOf;
+type AvatarSizeName = ValueOf;
+type EReceiptColorName = ValueOf;
+type AvatarSizeValue = ValueOf<
+ Pick<
+ typeof variables,
+ | 'avatarSizeNormal'
+ | 'avatarSizeSmallSubscript'
+ | 'avatarSizeMidSubscript'
+ | 'avatarSizeSubscript'
+ | 'avatarSizeSmall'
+ | 'avatarSizeSmaller'
+ | 'avatarSizeXLarge'
+ | 'avatarSizeLarge'
+ | 'avatarSizeMedium'
+ | 'avatarSizeLargeBordered'
+ | 'avatarSizeHeader'
+ | 'avatarSizeMentionIcon'
+ | 'avatarSizeSmallNormal'
+ >
+>;
+
+type AvatarStyle = {
+ width: number;
+ height: number;
+ borderRadius: number;
+ backgroundColor: string;
+};
+
+type ButtonSizeValue = ValueOf;
+type ButtonStateName = ValueOf;
+type AvatarSize = {width: number};
+
+type WorkspaceColorStyle = {backgroundColor: ColorValue; fill: ColorValue};
+type EreceiptColorStyle = {backgroundColor: ColorValue; color: ColorValue};
+
+export type {
+ AllStyles,
+ ParsableStyle,
+ ColorValue,
+ AvatarSizeName,
+ EReceiptColorName,
+ AvatarSizeValue,
+ AvatarStyle,
+ ButtonSizeValue,
+ ButtonStateName,
+ AvatarSize,
+ WorkspaceColorStyle,
+ EreceiptColorStyle,
+};
diff --git a/src/styles/utilities/userSelect/index.native.ts b/src/styles/utils/userSelect/index.native.ts
similarity index 100%
rename from src/styles/utilities/userSelect/index.native.ts
rename to src/styles/utils/userSelect/index.native.ts
diff --git a/src/styles/utilities/userSelect/index.ts b/src/styles/utils/userSelect/index.ts
similarity index 100%
rename from src/styles/utilities/userSelect/index.ts
rename to src/styles/utils/userSelect/index.ts
diff --git a/src/styles/utilities/userSelect/types.ts b/src/styles/utils/userSelect/types.ts
similarity index 100%
rename from src/styles/utilities/userSelect/types.ts
rename to src/styles/utils/userSelect/types.ts
diff --git a/src/styles/utilities/visibility/index.native.ts b/src/styles/utils/visibility/index.native.ts
similarity index 100%
rename from src/styles/utilities/visibility/index.native.ts
rename to src/styles/utils/visibility/index.native.ts
diff --git a/src/styles/utilities/visibility/index.ts b/src/styles/utils/visibility/index.ts
similarity index 100%
rename from src/styles/utilities/visibility/index.ts
rename to src/styles/utils/visibility/index.ts
diff --git a/src/styles/utilities/visibility/types.ts b/src/styles/utils/visibility/types.ts
similarity index 100%
rename from src/styles/utilities/visibility/types.ts
rename to src/styles/utils/visibility/types.ts
diff --git a/src/styles/utilities/whiteSpace/index.native.ts b/src/styles/utils/whiteSpace/index.native.ts
similarity index 100%
rename from src/styles/utilities/whiteSpace/index.native.ts
rename to src/styles/utils/whiteSpace/index.native.ts
diff --git a/src/styles/utilities/whiteSpace/index.ts b/src/styles/utils/whiteSpace/index.ts
similarity index 100%
rename from src/styles/utilities/whiteSpace/index.ts
rename to src/styles/utils/whiteSpace/index.ts
diff --git a/src/styles/utilities/whiteSpace/types.ts b/src/styles/utils/whiteSpace/types.ts
similarity index 100%
rename from src/styles/utilities/whiteSpace/types.ts
rename to src/styles/utils/whiteSpace/types.ts
diff --git a/src/styles/utilities/wordBreak/index.native.ts b/src/styles/utils/wordBreak/index.native.ts
similarity index 100%
rename from src/styles/utilities/wordBreak/index.native.ts
rename to src/styles/utils/wordBreak/index.native.ts
diff --git a/src/styles/utilities/wordBreak/index.ts b/src/styles/utils/wordBreak/index.ts
similarity index 100%
rename from src/styles/utilities/wordBreak/index.ts
rename to src/styles/utils/wordBreak/index.ts
diff --git a/src/styles/utilities/wordBreak/types.ts b/src/styles/utils/wordBreak/types.ts
similarity index 100%
rename from src/styles/utilities/wordBreak/types.ts
rename to src/styles/utils/wordBreak/types.ts
diff --git a/src/styles/utilities/writingDirection.ts b/src/styles/utils/writingDirection.ts
similarity index 100%
rename from src/styles/utilities/writingDirection.ts
rename to src/styles/utils/writingDirection.ts
diff --git a/src/types/modules/material-top-tabs.d.ts b/src/types/modules/material-top-tabs.d.ts
new file mode 100644
index 000000000000..693d82d07c57
--- /dev/null
+++ b/src/types/modules/material-top-tabs.d.ts
@@ -0,0 +1,8 @@
+/** This direct import is required, because this function was added by a patch,
+ * and its typings are not supported by default */
+import {useTabAnimation} from '@react-navigation/material-top-tabs/src/utils/useTabAnimation';
+
+declare module '@react-navigation/material-top-tabs' {
+ // eslint-disable-next-line import/prefer-default-export
+ export {useTabAnimation};
+}
diff --git a/src/types/onyx/Session.ts b/src/types/onyx/Session.ts
index faaa493b1286..aa4d52075d34 100644
--- a/src/types/onyx/Session.ts
+++ b/src/types/onyx/Session.ts
@@ -27,6 +27,9 @@ type Session = {
/** Server side errors keyed by microtime */
errors?: OnyxCommon.Errors;
+
+ /** User signed in with short lived token */
+ signedInWithShortLivedAuthToken?: boolean;
};
export default Session;
diff --git a/tests/e2e/config.js b/tests/e2e/config.js
index e6d6dafb1b27..41c1668fb6ba 100644
--- a/tests/e2e/config.js
+++ b/tests/e2e/config.js
@@ -10,6 +10,7 @@ const TEST_NAMES = {
AppStartTime: 'App start time',
OpenSearchPage: 'Open search page TTI',
ReportTyping: 'Report typing',
+ ChatOpening: 'Chat opening',
};
/**
@@ -78,5 +79,8 @@ module.exports = {
autoFocus: true,
},
},
+ [TEST_NAMES.ChatOpening]: {
+ name: TEST_NAMES.ChatOpening,
+ },
},
};
diff --git a/tests/ui/UnreadIndicatorsTest.js b/tests/ui/UnreadIndicatorsTest.js
index ed44d3088ae0..93bb2cb94d30 100644
--- a/tests/ui/UnreadIndicatorsTest.js
+++ b/tests/ui/UnreadIndicatorsTest.js
@@ -20,7 +20,7 @@ import * as Pusher from '../../src/libs/Pusher/pusher';
import PusherConnectionManager from '../../src/libs/PusherConnectionManager';
import ONYXKEYS from '../../src/ONYXKEYS';
import appSetup from '../../src/setup';
-import fontWeightBold from '../../src/styles/fontWeight/bold';
+import fontWeightBold from '../../src/styles/utils/fontWeight/bold';
import * as TestHelper from '../utils/TestHelper';
import waitForBatchedUpdates from '../utils/waitForBatchedUpdates';
import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct';
diff --git a/tests/unit/OptionsListUtilsTest.js b/tests/unit/OptionsListUtilsTest.js
index 45ddf44b244a..999107f0b3ae 100644
--- a/tests/unit/OptionsListUtilsTest.js
+++ b/tests/unit/OptionsListUtilsTest.js
@@ -1291,65 +1291,6 @@ describe('OptionsListUtils', () => {
},
];
- const smallTagsListWithParentChild = {
- Movies: {
- enabled: true,
- name: 'Movies',
- },
- 'Movies: Avengers: Endgame': {
- enabled: true,
- name: 'Movies: Avengers: Endgame',
- unencodedName: 'Movies: Avengers: Endgame',
- },
- Places: {
- enabled: false,
- name: 'Places',
- },
- Task: {
- enabled: true,
- name: 'Task',
- },
- };
-
- const smallResultListWithParentChild = [
- {
- title: '',
- shouldShow: false,
- indexOffset: 0,
- // data sorted alphabetically by name
- data: [
- {
- text: 'Movies',
- keyForList: 'Movies',
- searchText: 'Movies',
- tooltipText: 'Movies',
- isDisabled: false,
- },
- {
- text: ' Avengers',
- keyForList: 'Movies: Avengers',
- searchText: 'Movies: Avengers',
- tooltipText: 'Avengers',
- isDisabled: true,
- },
- {
- text: ' Endgame',
- keyForList: 'Movies: Avengers: Endgame',
- searchText: 'Movies: Avengers: Endgame',
- tooltipText: 'Endgame',
- isDisabled: false,
- },
- {
- text: 'Task',
- keyForList: 'Task',
- searchText: 'Task',
- tooltipText: 'Task',
- isDisabled: false,
- },
- ],
- },
- ];
-
const smallResult = OptionsListUtils.getFilteredOptions(REPORTS, PERSONAL_DETAILS, [], emptySearch, [], [], false, false, false, {}, [], true, smallTagsList);
expect(smallResult.tagOptions).toStrictEqual(smallResultList);
@@ -1412,26 +1353,9 @@ describe('OptionsListUtils', () => {
recentlyUsedTags,
);
expect(largeWrongSearchResult.tagOptions).toStrictEqual(largeWrongSearchResultList);
-
- const smallResultWithParentChild = OptionsListUtils.getFilteredOptions(
- REPORTS,
- PERSONAL_DETAILS,
- [],
- emptySearch,
- [],
- [],
- false,
- false,
- false,
- {},
- [],
- true,
- smallTagsListWithParentChild,
- );
- expect(smallResultWithParentChild.tagOptions).toStrictEqual(smallResultListWithParentChild);
});
- it('getIndentedOptionTree()', () => {
+ it('getCategoryOptionTree()', () => {
const categories = {
Meals: {
enabled: true,
@@ -1744,8 +1668,8 @@ describe('OptionsListUtils', () => {
},
];
- expect(OptionsListUtils.getIndentedOptionTree(categories)).toStrictEqual(result);
- expect(OptionsListUtils.getIndentedOptionTree(categories, true)).toStrictEqual(resultOneLine);
+ expect(OptionsListUtils.getCategoryOptionTree(categories)).toStrictEqual(result);
+ expect(OptionsListUtils.getCategoryOptionTree(categories, true)).toStrictEqual(resultOneLine);
});
it('sortCategories', () => {
diff --git a/tests/unit/ReportActionsUtilsTest.js b/tests/unit/ReportActionsUtilsTest.js
index b8b6eb5e7673..545d442e4799 100644
--- a/tests/unit/ReportActionsUtilsTest.js
+++ b/tests/unit/ReportActionsUtilsTest.js
@@ -288,7 +288,7 @@ describe('ReportActionsUtils', () => {
expect(result).toStrictEqual(input);
});
- it('should filter out deleted, non-pending comments', () => {
+ it('should filter out deleted and delete-pending comments', () => {
const input = [
{
created: '2022-11-13 22:27:01.825',
@@ -312,6 +312,7 @@ describe('ReportActionsUtils', () => {
];
const result = ReportActionsUtils.getSortedReportActionsForDisplay(input);
input.pop();
+ input.pop();
expect(result).toStrictEqual(input);
});
});
diff --git a/workflow_tests/assertions/createNewVersionAssertions.js b/workflow_tests/assertions/createNewVersionAssertions.js
index 27c54e924975..e4526ae59be2 100644
--- a/workflow_tests/assertions/createNewVersionAssertions.js
+++ b/workflow_tests/assertions/createNewVersionAssertions.js
@@ -26,19 +26,7 @@ const assertCreateNewVersionJobExecuted = (workflowResult, semverLevel = 'BUILD'
],
[],
),
- utils.createStepAssertion(
- 'Setup git for OSBotify',
- true,
- null,
- 'CREATENEWVERSION',
- 'Setup git for OSBotify',
- [
- {key: 'GPG_PASSPHRASE', value: '***'},
- {key: 'OS_BOTIFY_APP_ID', value: '***'},
- {key: 'OS_BOTIFY_PRIVATE_KEY', value: '***'},
- ],
- [],
- ),
+ utils.createStepAssertion('Setup git for OSBotify', true, null, 'CREATENEWVERSION', 'Setup git for OSBotify', [{key: 'GPG_PASSPHRASE', value: '***'}], []),
utils.createStepAssertion(
'Generate version',
true,
@@ -46,7 +34,7 @@ const assertCreateNewVersionJobExecuted = (workflowResult, semverLevel = 'BUILD'
'CREATENEWVERSION',
'Generate version',
[
- {key: 'GITHUB_TOKEN', value: 'os_botify_api_token'},
+ {key: 'GITHUB_TOKEN', value: '***'},
{key: 'SEMVER_LEVEL', value: semverLevel},
],
[],
diff --git a/workflow_tests/mocks/createNewVersionMocks.js b/workflow_tests/mocks/createNewVersionMocks.js
index 5e82e2102ef0..a1f601aef47f 100644
--- a/workflow_tests/mocks/createNewVersionMocks.js
+++ b/workflow_tests/mocks/createNewVersionMocks.js
@@ -21,9 +21,8 @@ const CREATENEWVERSION__CREATENEWVERSION__SETUP_GIT_FOR_OSBOTIFY__STEP_MOCK = ut
'Setup git for OSBotify',
'Setup git for OSBotify',
'CREATENEWVERSION',
- ['GPG_PASSPHRASE', 'OS_BOTIFY_APP_ID', 'OS_BOTIFY_PRIVATE_KEY'],
+ ['GPG_PASSPHRASE'],
[],
- {OS_BOTIFY_API_TOKEN: 'os_botify_api_token'},
);
const CREATENEWVERSION__CREATENEWVERSION__GENERATE_VERSION__STEP_MOCK = utils.createMockStep(
'Generate version',