diff --git a/.eslintrc.js b/.eslintrc.js index d9e25cc596f7..761a62b8314b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -108,7 +108,7 @@ module.exports = { 'plugin:you-dont-need-lodash-underscore/all', 'plugin:prettier/recommended', ], - plugins: ['@typescript-eslint', 'jsdoc', 'you-dont-need-lodash-underscore', 'react-native-a11y', 'react', 'testing-library', 'eslint-plugin-react-compiler', 'lodash'], + plugins: ['@typescript-eslint', 'jsdoc', 'you-dont-need-lodash-underscore', 'react-native-a11y', 'react', 'testing-library', 'eslint-plugin-react-compiler', 'lodash', 'deprecation'], ignorePatterns: ['lib/**'], parser: '@typescript-eslint/parser', parserOptions: { @@ -177,6 +177,7 @@ module.exports = { // ESLint core rules 'es/no-nullish-coalescing-operators': 'off', 'es/no-optional-chaining': 'off', + 'deprecation/deprecation': 'off', // Import specific rules 'import/consistent-type-specifier-style': ['error', 'prefer-top-level'], diff --git a/.github/actions/javascript/markPullRequestsAsDeployed/action.yml b/.github/actions/javascript/markPullRequestsAsDeployed/action.yml index f0ca77bdbf00..40dfc05e5448 100644 --- a/.github/actions/javascript/markPullRequestsAsDeployed/action.yml +++ b/.github/actions/javascript/markPullRequestsAsDeployed/action.yml @@ -14,7 +14,6 @@ inputs: GITHUB_TOKEN: description: "Github token for authentication" required: true - default: "${{ github.token }}" ANDROID: description: "Android job result ('success', 'failure', 'cancelled', or 'skipped')" required: true @@ -27,6 +26,12 @@ inputs: WEB: description: "Web job result ('success', 'failure', 'cancelled', or 'skipped')" required: true + DATE: + description: "The date of deployment" + required: false + NOTE: + description: "Additional note from the deployer" + required: false runs: using: "node20" main: "./index.js" diff --git a/.github/actions/javascript/markPullRequestsAsDeployed/index.js b/.github/actions/javascript/markPullRequestsAsDeployed/index.js index 9f97e4a72d20..b38b04141395 100644 --- a/.github/actions/javascript/markPullRequestsAsDeployed/index.js +++ b/.github/actions/javascript/markPullRequestsAsDeployed/index.js @@ -12713,9 +12713,15 @@ async function run() { const desktopResult = getDeployTableMessage(core.getInput('DESKTOP', { required: true })); const iOSResult = getDeployTableMessage(core.getInput('IOS', { required: true })); const webResult = getDeployTableMessage(core.getInput('WEB', { required: true })); + const date = core.getInput('DATE'); + const note = core.getInput('NOTE'); function getDeployMessage(deployer, deployVerb, prTitle) { let message = `🚀 [${deployVerb}](${workflowURL}) to ${isProd ? 'production' : 'staging'}`; - message += ` by https://github.com/${deployer} in version: ${version} 🚀`; + message += ` by https://github.com/${deployer} in version: ${version} `; + if (date) { + message += `on ${date}`; + } + message += `🚀`; message += `\n\nplatform | result\n---|---\n🤖 android 🤖|${androidResult}\n🖥 desktop 🖥|${desktopResult}`; message += `\n🍎 iOS 🍎|${iOSResult}\n🕸 web 🕸|${webResult}`; if (deployVerb === 'Cherry-picked' && !/no ?qa/gi.test(prTitle ?? '')) { @@ -12723,6 +12729,9 @@ async function run() { message += '\n\n@Expensify/applauseleads please QA this PR and check it off on the [deploy checklist](https://github.com/Expensify/App/issues?q=is%3Aopen+is%3Aissue+label%3AStagingDeployCash) if it passes.'; } + if (note) { + message += `\n\n_Note:_ ${note}`; + } return message; } if (isProd) { diff --git a/.github/actions/javascript/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts b/.github/actions/javascript/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts index 71a5c7d5c6ee..e6424c89833a 100644 --- a/.github/actions/javascript/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts +++ b/.github/actions/javascript/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts @@ -55,9 +55,16 @@ async function run() { const iOSResult = getDeployTableMessage(core.getInput('IOS', {required: true}) as PlatformResult); const webResult = getDeployTableMessage(core.getInput('WEB', {required: true}) as PlatformResult); + const date = core.getInput('DATE'); + const note = core.getInput('NOTE'); + function getDeployMessage(deployer: string, deployVerb: string, prTitle?: string): string { let message = `🚀 [${deployVerb}](${workflowURL}) to ${isProd ? 'production' : 'staging'}`; - message += ` by https://github.com/${deployer} in version: ${version} 🚀`; + message += ` by https://github.com/${deployer} in version: ${version} `; + if (date) { + message += `on ${date}`; + } + message += `🚀`; message += `\n\nplatform | result\n---|---\n🤖 android 🤖|${androidResult}\n🖥 desktop 🖥|${desktopResult}`; message += `\n🍎 iOS 🍎|${iOSResult}\n🕸 web 🕸|${webResult}`; @@ -67,6 +74,10 @@ async function run() { '\n\n@Expensify/applauseleads please QA this PR and check it off on the [deploy checklist](https://github.com/Expensify/App/issues?q=is%3Aopen+is%3Aissue+label%3AStagingDeployCash) if it passes.'; } + if (note) { + message += `\n\n_Note:_ ${note}`; + } + return message; } diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 2ab19d13183a..53afe03720f7 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -649,34 +649,14 @@ jobs: GITHUB_TOKEN: ${{ github.token }} SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} - postGithubComment: - name: Post a GitHub comments on all deployed PRs when platforms are done building and deploying - runs-on: ubuntu-latest + postGithubComments: + uses: ./.github/workflows/postDeployComments.yml if: ${{ always() && fromJSON(needs.checkDeploymentSuccess.outputs.IS_AT_LEAST_ONE_PLATFORM_DEPLOYED) }} needs: [prep, android, desktop, iOS, web, checkDeploymentSuccess, createPrerelease, finalizeRelease] - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup Node - uses: ./.github/actions/composite/setupNode - - - name: Get Release Pull Request List - id: getReleasePRList - uses: ./.github/actions/javascript/getDeployPullRequestList - with: - TAG: ${{ needs.prep.outputs.APP_VERSION }} - GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }} - IS_PRODUCTION_DEPLOY: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} - - - name: Comment on issues - uses: ./.github/actions/javascript/markPullRequestsAsDeployed - with: - PR_LIST: ${{ steps.getReleasePRList.outputs.PR_LIST }} - IS_PRODUCTION_DEPLOY: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} - DEPLOY_VERSION: ${{ needs.prep.outputs.APP_VERSION }} - GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }} - ANDROID: ${{ needs.android.result }} - DESKTOP: ${{ needs.desktop.result }} - IOS: ${{ needs.iOS.result }} - WEB: ${{ needs.web.result }} + with: + version: ${{ needs.prep.outputs.APP_VERSION }} + env: ${{ github.ref == 'refs/heads/production' && 'production' || 'staging' }} + android: ${{ needs.android.result }} + ios: ${{ needs.iOS.result }} + web: ${{ needs.web.result }} + desktop: ${{ needs.desktop.result }} diff --git a/.github/workflows/postDeployComments.yml b/.github/workflows/postDeployComments.yml new file mode 100644 index 000000000000..3893d3cf3f7c --- /dev/null +++ b/.github/workflows/postDeployComments.yml @@ -0,0 +1,118 @@ +name: Post Deploy Comments + +on: + workflow_call: + inputs: + version: + description: The version that was deployed + required: true + type: string + env: + description: The environment that was deployed (staging or prod) + required: true + type: string + android: + description: Android deploy status + required: true + type: string + ios: + description: iOS deploy status + required: true + type: string + web: + description: Web deploy status + required: true + type: string + desktop: + description: Desktop deploy status + required: true + type: string + workflow_dispatch: + inputs: + version: + description: The version that was deployed + required: true + type: string + env: + description: The environment that was deployed (staging or prod) + required: true + type: choice + options: + - staging + - production + android: + description: Android deploy status + required: true + type: choice + options: + - success + - failure + - cancelled + - skipped + ios: + description: iOS deploy status + required: true + type: choice + options: + - success + - failure + - cancelled + - skipped + web: + description: Web deploy status + required: true + type: choice + options: + - success + - failure + - cancelled + - skipped + desktop: + description: Desktop deploy status + required: true + type: choice + options: + - success + - failure + - cancelled + - skipped + date: + description: The date when this deploy occurred + required: false + type: string + note: + description: Any additional note you want to include with the deploy comment + required: false + type: string + +jobs: + postDeployComments: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: ./.github/actions/composite/setupNode + + - name: Get pull request list + id: getPullRequestList + uses: ./.github/actions/javascript/getDeployPullRequestList + with: + TAG: ${{ inputs.version }} + GITHUB_TOKEN: ${{ github.token }} + IS_PRODUCTION_DEPLOY: ${{ inputs.env == 'production' }} + + - name: Comment on issues + uses: ./.github/actions/javascript/markPullRequestsAsDeployed + with: + PR_LIST: ${{ steps.getPullRequestList.outputs.PR_LIST }} + IS_PRODUCTION_DEPLOY: ${{ inputs.env == 'production' }} + DEPLOY_VERSION: ${{ inputs.version }} + GITHUB_TOKEN: ${{ github.token }} + ANDROID: ${{ inputs.android }} + DESKTOP: ${{ inputs.desktop }} + IOS: ${{ inputs.ios }} + WEB: ${{ inputs.web }} + DATE: ${{ inputs.date }} + NOTE: ${{ inputs.note }} diff --git a/README.md b/README.md index c8faff111bae..4a691045e7c2 100644 --- a/README.md +++ b/README.md @@ -619,7 +619,30 @@ Some pointers: key to the translation file and use the arrow function version, like so: `nameOfTheKey: ({amount, dateTime}) => "User has sent " + amount + " to you on " + dateTime,`. This is because the order of the phrases might vary from one language to another. - +- When working with translations that involve plural forms, it's important to handle different cases correctly. + + For example: + - zero: Used when there are no items **(optional)**. + - one: Used when there's exactly one item. + - two: Used when there's two items. **(optional)** + - few: Used for a small number of items **(optional)**. + - many: Used for larger quantities **(optional)**. + - other: A catch-all case for other counts or variations. + + Here’s an example of how to implement plural translations: + + messages: () => ({ + zero: 'No messages', + one: 'One message', + two: 'Two messages', + few: (count) => `${count} messages`, + many: (count) => `You have ${count} messages`, + other: (count) => `You have ${count} unread messages`, + }) + + In your code, you can use the translation like this: + + `translate('common.messages', {count: 1});` ---- # Deploying diff --git a/__mocks__/react-native-haptic-feedback.ts b/__mocks__/react-native-haptic-feedback.ts new file mode 100644 index 000000000000..6d20b410d835 --- /dev/null +++ b/__mocks__/react-native-haptic-feedback.ts @@ -0,0 +1,5 @@ +import type HapticFeedback from 'react-native-haptic-feedback'; + +const RNHapticFeedback: typeof HapticFeedback = {trigger: jest.fn()}; + +export default RNHapticFeedback; diff --git a/android/app/build.gradle b/android/app/build.gradle index 491c810cb350..9153966e1d5d 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -110,8 +110,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009003902 - versionName "9.0.39-2" + versionCode 1009004004 + versionName "9.0.40-4" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/android/build.gradle b/android/build.gradle index 85e547439cc1..fd3f9997612e 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -29,7 +29,7 @@ buildscript { classpath("com.google.firebase:firebase-crashlytics-gradle:2.7.1") classpath("com.google.firebase:perf-plugin:1.4.1") // Fullstory integration - classpath ("com.fullstory:gradle-plugin-local:1.49.0") + classpath ("com.fullstory:gradle-plugin-local:1.52.0") // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/android/gradle.properties b/android/gradle.properties index 87333d20f743..46cd98554d29 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -55,3 +55,5 @@ MYAPP_UPLOAD_KEY_ALIAS=ReactNativeChat-Key-Alias disableFrameProcessors=true android.nonTransitiveRClass=false + +org.gradle.parallel=true diff --git a/assets/images/table.svg b/assets/images/table.svg index a9cfe68f339e..36d4ced774f1 100644 --- a/assets/images/table.svg +++ b/assets/images/table.svg @@ -1,3 +1,3 @@ - + diff --git a/babel.config.js b/babel.config.js index 3721edaa7afb..663eb29d5d2f 100644 --- a/babel.config.js +++ b/babel.config.js @@ -21,6 +21,7 @@ const defaultPlugins = [ '@babel/transform-runtime', '@babel/plugin-proposal-class-properties', + ['@babel/plugin-transform-object-rest-spread', {useBuiltIns: true, loose: true}], // We use `@babel/plugin-transform-class-properties` for transforming ReactNative libraries and do not use it for our own // source code transformation as we do not use class property assignment. diff --git a/docs/articles/expensify-classic/bank-accounts-and-payments/bank-accounts/Enable-Global-Reimbursements.md b/docs/articles/expensify-classic/bank-accounts-and-payments/bank-accounts/Enable-Global-Reimbursements.md index b0c767fce277..37d8d8bbe42b 100644 --- a/docs/articles/expensify-classic/bank-accounts-and-payments/bank-accounts/Enable-Global-Reimbursements.md +++ b/docs/articles/expensify-classic/bank-accounts-and-payments/bank-accounts/Enable-Global-Reimbursements.md @@ -90,6 +90,10 @@ Have the employee double-check that their [default workspace](https://help.expen - **Authorized User**: The person who will process global reimbursements. The Authorized User should be the same person who manages the bank account connection in Expensify. - **User**: You can leave this section blank because the “User” is Expensify. +**Does Global Reimbursement support Sepa in the EU?** + +Global Reimbursement uses Sepa B2B to facilitate payments from EU-based accounts. Sepa Core is not supported. + {% include faq-end.md %} diff --git a/docs/articles/expensify-classic/connections/quickbooks-desktop/Connect-To-QuickBooks-Desktop.md b/docs/articles/expensify-classic/connections/quickbooks-desktop/Connect-To-QuickBooks-Desktop.md index 1fb1b09328b9..bda84eb0a49f 100644 --- a/docs/articles/expensify-classic/connections/quickbooks-desktop/Connect-To-QuickBooks-Desktop.md +++ b/docs/articles/expensify-classic/connections/quickbooks-desktop/Connect-To-QuickBooks-Desktop.md @@ -26,9 +26,7 @@ To connect QuickBooks Desktop to Expensify, you must log into QuickBooks Desktop 7. Download the Web Connector and go through the guided installation process. 8. Open the Web Connector. -9. Click on **Add an Application**. - - ![The Web Connnector Pop-up where you will need to click on Add an Application](https://help.expensify.com/assets/images/QBO_desktop_03.png){:width="100%"} +9. Download the config file when prompted during the setup process, then open it using your File Explorer. This will automatically load the application into the QuickBooks Web Connector. {% include info.html %} For this step, it is key to ensure that the correct company file is open in QuickBooks Desktop and that it is the only one open. diff --git a/docs/articles/new-expensify/expenses-&-payments/Connect-a-Personal-Bank-Account.md b/docs/articles/new-expensify/expenses-&-payments/Connect-a-Personal-Bank-Account.md index b8e66c937a0a..2d33552f3e3a 100644 --- a/docs/articles/new-expensify/expenses-&-payments/Connect-a-Personal-Bank-Account.md +++ b/docs/articles/new-expensify/expenses-&-payments/Connect-a-Personal-Bank-Account.md @@ -8,7 +8,10 @@ Connecting a personal bank account to Expensify allows you to get reimbursed for 1. Click your profile image or icon in the bottom left menu. 2. Click **Wallet**. 3. Click **Add Bank Account**. -4. Click **Continue** (this will open a new window and redirect you to Plaid). + + ![Wallet Settings, showing where to connect a bank account](https://help.expensify.com/assets/images/addbankaccount_01.png){:width="100%"} + +5. Click **Continue** (this will open a new window and redirect you to Plaid). {% include info.html %} Plaid is an encrypted third-party financial data platform that Expensify uses to securely verify your banking information. @@ -19,4 +22,6 @@ Plaid is an encrypted third-party financial data platform that Expensify uses to 7. Choose which account you want to connect to Expensify. 8. Click **Save & continue**. + ![Wallet Settings, showing bank account connected](https://help.expensify.com/assets/images/addbankaccount_03.png){:width="100%"} + Once connected, all payments and reimbursements will be deposited directly into that bank account. diff --git a/docs/articles/new-expensify/workspaces/Add-approvals.md b/docs/articles/new-expensify/workspaces/Add-approvals.md new file mode 100644 index 000000000000..5d8c1f733287 --- /dev/null +++ b/docs/articles/new-expensify/workspaces/Add-approvals.md @@ -0,0 +1,73 @@ +--- +title: Add approvals +description: Add approvals to your workspace to require additional approval before authorizing payments. +--- +
+ +# Add approvals + +Each Expensify workspace can be configured to require additional approvals before payments are authorized. Once approvals are enabled on a workspace, admins will be able to set a default approval workflow to apply to all members of the workspace, as well as set custom approval workflows for specific members. + +When workspace members submit expenses, the expenses will require approval from each approver in their approval workflow before payment is authorized. + +## Add approvals on a workspace + +**To enable Add approvals on a workspace you are an admin on:** + +1. Click your profile image or icon in the bottom left menu +2. Click **Workspaces** in the left menu +3. Select the workspace where you want to add approvals +4. Click **Workflows** in the left menu +5. Click the toggle next to **Add approvals** + +Toggling on **Add approvals** will reveal an option to set a default approval workflow. + +## Configure approval workflows + +**To configure the default approval workflow for the workspace:** + +1. Click your profile image or icon in the bottom left menu +2. Click **Workspaces** in the left menu +3. Select the workspace where you want to set the approval workflow +4. Click **Workflows** in the left menu +5. Under **Expenses from Everyone**, click on **First approver** +6. Select the workspace member who should be the first approver in the approval workflow +7. Under **Additional approver**, continue selecting workspace members until all the desired approvers are listed +8. Click **Save** + +Note: When Add approvals is enabled, the workspace must have a default approval workflow. + +**To set an approval workflow that applies only to specific workspace members:** + +1. Click your profile image or icon in the bottom left menu +2. Click **Workspaces** in the left menu +3. Select the workspace where you want to add approvals +4. Click **Workflows** in the left menu +5. Under **Add approvals**, click on **Add approval workflow** +6. Choose the workspace member whose expenses should go through the custom approval workfow +7. Click **Next** +8. Choose the workspace member who should be the first approver on submitted expenses in the approval workflow +9. Click **Next** +10. Click **Additional approver** to continue selecting workspace members until all the desired approvers are listed +11. Click **Add workflow** to save it + +## Edit or delete approval workflows + +**To edit an approval workflow:** + +1. On the **Workflows** page, click the approval workflow that should be edited +2. Click on the Approver field for the approval level where the edit should be made +3. Choose the workspace member who should be set as the approver for that level, or deselect them to remove the approval level from the workflow +4. Click **Save** + +**To delete an approval workflow:** + +1. On the **Workflows** page, click the approval workflow that shoudld be deleted +2. Click **Delete** +3. In the window that appears,click **Delete** again + +# FAQ + +## Can an employee have more than one approval workflow? +No, each employee can have only one approval workflow + diff --git a/docs/articles/new-expensify/workspaces/Set-distance-rates.md b/docs/articles/new-expensify/workspaces/Set-distance-rates.md new file mode 100644 index 000000000000..c434f34d2cef --- /dev/null +++ b/docs/articles/new-expensify/workspaces/Set-distance-rates.md @@ -0,0 +1,50 @@ +--- +title: Set Distance Rates +description: Set distance rates on your Expensify workspace +--- +
+ +# Set Distance eates + +Each Expensify workspace can be configured with one or more distance rates. Once distance rates are enabled on your workspace, employees will be able to choose between the available rates to create distance expenses. + +## Enable distance rates on a workspace + +**To enable distance rates on a workspace you are an admin on:** + +1. Click your profile image or icon in the bottom left menu +2. Click **Workspaces** in the left menu +3. Select the workspace where you want to enable distance rates +4. Click **More features** in the left menu +5. Click the toggle next to **Distance rates** + +After toggling on distance rates, you will see a new **Distance rates** option in the left menu. + +## Add, delete, or edit distance rates + +**To add a distance rate:** + +1. Click your profile image or icon in the bottom left menu +2. Click **Workspaces** in the left menu +3. Select the workspace where you want to add distance rates +4. Click **Distance rates** in the left menu +5. Click **Add rate** in the top right +6. Enter a value, then click **Save** + +**To enable, disable, edit or delete a single distance rate:** + +1. Click the distance rate on the **Distance rates** settings page +2. To enable or disable the distance rate, click the toggle next to **Enable rate**, then click **Save** +3. To edit the rate amount, click on the amount field, enter the new value, then click **Save** +4. To permanently delete the distance rate, click **Delete** + +Note: When Distance rates is enabled, the workspace must have at least one enabled distance rate. + +**To enable, disable, edit or delete distance rates in bulk:** + +1. On the **Distance rates** settings page, click the checkboxes next to the distance rates that should me modified +2. Click “x selected” at the top right +3. To enable or disable all the selected distance rates, click **Enable rates** or **Disable rates** +4. To permanently delete the distance rates, click **Delete rates** + +Note: When Distance rates are enabled, the workspace must have at least one enabled distance rate. diff --git a/docs/assets/images/invoices_01.png b/docs/assets/images/invoices_01.png new file mode 100644 index 000000000000..fc6d5587bb03 Binary files /dev/null and b/docs/assets/images/invoices_01.png differ diff --git a/docs/assets/images/invoices_02.png b/docs/assets/images/invoices_02.png new file mode 100644 index 000000000000..29038987c18a Binary files /dev/null and b/docs/assets/images/invoices_02.png differ diff --git a/docs/assets/images/invoices_03.png b/docs/assets/images/invoices_03.png new file mode 100644 index 000000000000..fd78aa731784 Binary files /dev/null and b/docs/assets/images/invoices_03.png differ diff --git a/docs/assets/images/invoices_04.png b/docs/assets/images/invoices_04.png new file mode 100644 index 000000000000..d2e301a9d1a5 Binary files /dev/null and b/docs/assets/images/invoices_04.png differ diff --git a/docs/assets/images/invoices_05.png b/docs/assets/images/invoices_05.png new file mode 100644 index 000000000000..8eae5efaa9df Binary files /dev/null and b/docs/assets/images/invoices_05.png differ diff --git a/docs/assets/images/invoices_06.png b/docs/assets/images/invoices_06.png new file mode 100644 index 000000000000..2858227891eb Binary files /dev/null and b/docs/assets/images/invoices_06.png differ diff --git a/ios/NewApp_AdHoc.mobileprovision.gpg b/ios/NewApp_AdHoc.mobileprovision.gpg index 78fb5d53d9e9..d2f181f6b7f4 100644 Binary files a/ios/NewApp_AdHoc.mobileprovision.gpg and b/ios/NewApp_AdHoc.mobileprovision.gpg differ diff --git a/ios/NewApp_AdHoc_Notification_Service.mobileprovision.gpg b/ios/NewApp_AdHoc_Notification_Service.mobileprovision.gpg index ac2c76a118d5..c0afa40ecb29 100644 Binary files a/ios/NewApp_AdHoc_Notification_Service.mobileprovision.gpg and b/ios/NewApp_AdHoc_Notification_Service.mobileprovision.gpg differ diff --git a/ios/NewExpensify.xcodeproj/project.pbxproj b/ios/NewExpensify.xcodeproj/project.pbxproj index 768062717d4b..1a29a275b956 100644 --- a/ios/NewExpensify.xcodeproj/project.pbxproj +++ b/ios/NewExpensify.xcodeproj/project.pbxproj @@ -43,7 +43,7 @@ D27CE6B77196EF3EF450EEAC /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 0D3F9E814828D91464DF9D35 /* PrivacyInfo.xcprivacy */; }; DD79042B2792E76D004484B4 /* RCTBootSplash.mm in Sources */ = {isa = PBXBuildFile; fileRef = DD79042A2792E76D004484B4 /* RCTBootSplash.mm */; }; DDCB2E57F334C143AC462B43 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D20D83B0E39BA6D21761E72 /* ExpoModulesProvider.swift */; }; - E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; + E51DC681C7DEE40AEBDDFBFE /* (null) in Frameworks */ = {isa = PBXBuildFile; }; E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9DF872C2525201700607FDC /* AirshipConfig.plist */; }; ED222ED90E074A5481A854FA /* ExpensifyNeue-BoldItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = 8B28D84EF339436DBD42A203 /* ExpensifyNeue-BoldItalic.otf */; }; F0C450EA2705020500FD2970 /* colors.json in Resources */ = {isa = PBXBuildFile; fileRef = F0C450E92705020500FD2970 /* colors.json */; }; @@ -131,6 +131,7 @@ 7F3784A52C7512CF00063508 /* NewExpensifyReleaseDevelopment.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = NewExpensifyReleaseDevelopment.entitlements; path = NewExpensify/NewExpensifyReleaseDevelopment.entitlements; sourceTree = ""; }; 7F3784A62C7512D900063508 /* NewExpensifyReleaseAdHoc.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = NewExpensifyReleaseAdHoc.entitlements; path = NewExpensify/NewExpensifyReleaseAdHoc.entitlements; sourceTree = ""; }; 7F3784A72C75131000063508 /* NewExpensifyReleaseProduction.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = NewExpensifyReleaseProduction.entitlements; path = NewExpensify/NewExpensifyReleaseProduction.entitlements; sourceTree = ""; }; + 7F9C91352CA5EC4900FC4DC1 /* NotificationServiceExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotificationServiceExtension.entitlements; sourceTree = ""; }; 7F9DD8D92B2A445B005E3AFA /* ExpError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpError.swift; sourceTree = ""; }; 7FD73C9B2B23CE9500420AF3 /* NotificationServiceExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationServiceExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 7FD73C9D2B23CE9500420AF3 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; @@ -175,8 +176,8 @@ buildActionMask = 2147483647; files = ( 383643682B6D4AE2005BB9AE /* DeviceCheck.framework in Frameworks */, - E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */, - E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */, + E51DC681C7DEE40AEBDDFBFE /* (null) in Frameworks */, + E51DC681C7DEE40AEBDDFBFE /* (null) in Frameworks */, 8744C5400E24E379441C04A4 /* libPods-NewExpensify.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -266,6 +267,7 @@ 7FD73C9C2B23CE9500420AF3 /* NotificationServiceExtension */ = { isa = PBXGroup; children = ( + 7F9C91352CA5EC4900FC4DC1 /* NotificationServiceExtension.entitlements */, 7FD73C9D2B23CE9500420AF3 /* NotificationService.swift */, 7FD73C9F2B23CE9500420AF3 /* Info.plist */, 7F9DD8D92B2A445B005E3AFA /* ExpError.swift */, @@ -1183,6 +1185,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; @@ -1347,6 +1350,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; @@ -1433,6 +1437,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; @@ -1518,8 +1523,9 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; @@ -1560,7 +1566,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.chat.expensify.chat.NotificationServiceExtension; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "(NewApp) Development: Notification Service"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "(NewApp) AppStore: Notification Service"; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; @@ -1604,6 +1610,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; @@ -1683,6 +1690,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; @@ -1761,6 +1769,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 7bed47c032af..af696a13c998 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 9.0.39 + 9.0.40 CFBundleSignature ???? CFBundleURLTypes @@ -40,7 +40,7 @@ CFBundleVersion - 9.0.39.2 + 9.0.40.4 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 6e01c0d2f912..0795209286ed 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 9.0.39 + 9.0.40 CFBundleSignature ???? CFBundleVersion - 9.0.39.2 + 9.0.40.4 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 74158e9d574a..a545bd82c164 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -11,9 +11,9 @@ CFBundleName $(PRODUCT_NAME) CFBundleShortVersionString - 9.0.39 + 9.0.40 CFBundleVersion - 9.0.39.2 + 9.0.40.4 NSExtension NSExtensionPointIdentifier diff --git a/ios/NotificationServiceExtension/NotificationServiceExtension.entitlements b/ios/NotificationServiceExtension/NotificationServiceExtension.entitlements new file mode 100644 index 000000000000..f52d3207d6e3 --- /dev/null +++ b/ios/NotificationServiceExtension/NotificationServiceExtension.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.com.expensify.new + + + diff --git a/ios/Podfile b/ios/Podfile index 2ed1752abf4f..e807089c26b9 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -121,4 +121,4 @@ target 'NotificationServiceExtension' do pod 'AirshipServiceExtension' end -pod 'FullStory', :http => 'https://ios-releases.fullstory.com/fullstory-1.49.0-xcframework.tar.gz' \ No newline at end of file +pod 'FullStory', :http => 'https://ios-releases.fullstory.com/fullstory-1.52.0-xcframework.tar.gz' \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 0f1a42791d1e..beac64acd083 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -143,8 +143,8 @@ PODS: - GoogleUtilities/Environment (~> 7.7) - "GoogleUtilities/NSData+zlib (~> 7.7)" - fmt (9.1.0) - - FullStory (1.49.0) - - fullstory_react-native (1.4.2): + - FullStory (1.52.0) + - fullstory_react-native (1.7.1): - DoubleConversion - FullStory (~> 1.14) - glog @@ -2451,7 +2451,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNReactNativeHapticFeedback (2.3.1): + - RNReactNativeHapticFeedback (2.3.3): - DoubleConversion - glog - hermes-engine @@ -2700,7 +2700,7 @@ DEPENDENCIES: - ExpoModulesCore (from `../node_modules/expo-modules-core`) - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`) - - "FullStory (from `{:http=>\"https://ios-releases.fullstory.com/fullstory-1.49.0-xcframework.tar.gz\"}`)" + - "FullStory (from `{:http=>\"https://ios-releases.fullstory.com/fullstory-1.52.0-xcframework.tar.gz\"}`)" - "fullstory_react-native (from `../node_modules/@fullstory/react-native`)" - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) @@ -2874,7 +2874,7 @@ EXTERNAL SOURCES: fmt: :podspec: "../node_modules/react-native/third-party-podspecs/fmt.podspec" FullStory: - :http: https://ios-releases.fullstory.com/fullstory-1.49.0-xcframework.tar.gz + :http: https://ios-releases.fullstory.com/fullstory-1.52.0-xcframework.tar.gz fullstory_react-native: :path: "../node_modules/@fullstory/react-native" glog: @@ -3089,7 +3089,7 @@ EXTERNAL SOURCES: CHECKOUT OPTIONS: FullStory: - :http: https://ios-releases.fullstory.com/fullstory-1.49.0-xcframework.tar.gz + :http: https://ios-releases.fullstory.com/fullstory-1.52.0-xcframework.tar.gz SPEC CHECKSUMS: Airship: bb32ff2c5a811352da074480357d9f02dbb8f327 @@ -3116,8 +3116,8 @@ SPEC CHECKSUMS: FirebasePerformance: 0c01a7a496657d7cea86d40c0b1725259d164c6c FirebaseRemoteConfig: 2d6e2cfdb49af79535c8af8a80a4a5009038ec2b fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120 - FullStory: c95f74445f871bc344cdc4a4e4ece61b5554e55d - fullstory_react-native: 1818ee93dc38801665f26869f7ad68abb698a89a + FullStory: c8a10b2358c0d33c57be84d16e4c440b0434b33d + fullstory_react-native: 44dc2c85a6316df2713e6cb0048ce5719c3b0bab glog: 69ef571f3de08433d766d614c73a9838a06bf7eb GoogleAppMeasurement: 5ba1164e3c844ba84272555e916d0a6d3d977e91 GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a @@ -3233,7 +3233,7 @@ SPEC CHECKSUMS: RNLocalize: d4b8af4e442d4bcca54e68fc687a2129b4d71a81 rnmapbox-maps: 460d6ff97ae49c7d5708c3212c6521697c36a0c4 RNPermissions: 0b1429b55af59d1d08b75a8be2459f65a8ac3f28 - RNReactNativeHapticFeedback: 31833c3ef341d716dbbd9d64e940f0c230db46f6 + RNReactNativeHapticFeedback: 73756a3477a5a622fa16862a3ab0d0fc5e5edff5 RNReanimated: 76901886830e1032f16bbf820153f7dc3f02d51d RNScreens: de6e57426ba0e6cbc3fb5b4f496e7f08cb2773c2 RNShare: bd4fe9b95d1ee89a200778cc0753ebe650154bb0 @@ -3248,6 +3248,6 @@ SPEC CHECKSUMS: VisionCamera: c6c8aa4b028501fc87644550fbc35a537d4da3fb Yoga: a1d7895431387402a674fd0d1c04ec85e87909b8 -PODFILE CHECKSUM: e479ec84cb53e5fd463486d71dfee91708d3fd9a +PODFILE CHECKSUM: a07e55247056ec5d84d1af31d694506efff3cfe2 COCOAPODS: 1.15.2 diff --git a/package-lock.json b/package-lock.json index 3c27c44a3bd7..d43c8fee25c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.0.39-2", + "version": "9.0.40-4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.0.39-2", + "version": "9.0.40-4", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -50,7 +50,7 @@ "date-fns-tz": "^2.0.0", "dom-serializer": "^0.2.2", "domhandler": "^4.3.0", - "expensify-common": "2.0.84", + "expensify-common": "2.0.88", "expo": "51.0.31", "expo-av": "14.0.7", "expo-image": "1.12.15", @@ -85,7 +85,7 @@ "react-native-fs": "^2.20.0", "react-native-gesture-handler": "2.18.0", "react-native-google-places-autocomplete": "2.5.6", - "react-native-haptic-feedback": "^2.3.1", + "react-native-haptic-feedback": "^2.3.3", "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#cb392140db4953a283590d7cf93b4d0461baa2a9", "react-native-key-command": "^1.0.8", @@ -24037,9 +24037,9 @@ } }, "node_modules/expensify-common": { - "version": "2.0.84", - "resolved": "https://registry.npmjs.org/expensify-common/-/expensify-common-2.0.84.tgz", - "integrity": "sha512-VistjMexRz/1u1IqjIZwGRE7aS6QOat7420Dualn+NaqMHGkfeeB4uUR3RQhCtlDbcwFBKTryIGgSrrC0N1YpA==", + "version": "2.0.88", + "resolved": "https://registry.npmjs.org/expensify-common/-/expensify-common-2.0.88.tgz", + "integrity": "sha512-4k6X6BekydYSRWkWRMB/Ts0W5Zx3BskEpLQEuxpq+cW9QIvTyFliho/dMLaXYOqS6nMQuzkjJYqfGPx9agVnOg==", "dependencies": { "awesome-phonenumber": "^5.4.0", "classnames": "2.5.0", @@ -34543,9 +34543,9 @@ } }, "node_modules/react-native-haptic-feedback": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/react-native-haptic-feedback/-/react-native-haptic-feedback-2.3.1.tgz", - "integrity": "sha512-dPfjV4iVHfhVyfG+nRd88ygjahbdup7KFZDM5L2aNIAzqbNtKxHZn5O1pHegwSj1t15VJliu0GyTX7XpBDeXUw==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/react-native-haptic-feedback/-/react-native-haptic-feedback-2.3.3.tgz", + "integrity": "sha512-svS4D5PxfNv8o68m9ahWfwje5NqukM3qLS48+WTdhbDkNUkOhP9rDfDSRHzlhk4zq+ISjyw95EhLeh8NkKX5vQ==", "workspaces": [ "example" ], diff --git a/package.json b/package.json index 6f4980f04ee0..e8322960c61c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.0.39-2", + "version": "9.0.40-4", "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.", @@ -107,7 +107,7 @@ "date-fns-tz": "^2.0.0", "dom-serializer": "^0.2.2", "domhandler": "^4.3.0", - "expensify-common": "2.0.84", + "expensify-common": "2.0.88", "expo": "51.0.31", "expo-av": "14.0.7", "expo-image": "1.12.15", @@ -142,7 +142,7 @@ "react-native-fs": "^2.20.0", "react-native-gesture-handler": "2.18.0", "react-native-google-places-autocomplete": "2.5.6", - "react-native-haptic-feedback": "^2.3.1", + "react-native-haptic-feedback": "^2.3.3", "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#cb392140db4953a283590d7cf93b4d0461baa2a9", "react-native-key-command": "^1.0.8", diff --git a/patches/react-native-haptic-feedback+2.3.1.patch b/patches/react-native-haptic-feedback+2.3.1.patch deleted file mode 100644 index 799bdaf7e53e..000000000000 --- a/patches/react-native-haptic-feedback+2.3.1.patch +++ /dev/null @@ -1,56 +0,0 @@ -diff --git a/node_modules/react-native-haptic-feedback/ios/RNHapticFeedback/RNHapticFeedback.h b/node_modules/react-native-haptic-feedback/ios/RNHapticFeedback/RNHapticFeedback.h -index c1498b9..250df1f 100644 ---- a/node_modules/react-native-haptic-feedback/ios/RNHapticFeedback/RNHapticFeedback.h -+++ b/node_modules/react-native-haptic-feedback/ios/RNHapticFeedback/RNHapticFeedback.h -@@ -1,5 +1,5 @@ - #ifdef RCT_NEW_ARCH_ENABLED --#import "RNHapticFeedbackSpec.h" -+#import - - @interface RNHapticFeedback : NSObject - #else -diff --git a/node_modules/react-native-haptic-feedback/ios/RNHapticFeedback/RNHapticFeedbackSpec.h b/node_modules/react-native-haptic-feedback/ios/RNHapticFeedback/RNHapticFeedbackSpec.h -deleted file mode 100644 -index 6f0f81d..0000000 ---- a/node_modules/react-native-haptic-feedback/ios/RNHapticFeedback/RNHapticFeedbackSpec.h -+++ /dev/null -@@ -1,15 +0,0 @@ --// --// RNHapticFeedbackSpec.h --// RNHapticFeedback --// --// Created by Michael Kuczera on 05.08.24. --// Copyright © 2024 Facebook. All rights reserved. --// --#import -- --@protocol NativeHapticFeedbackSpec -- --// Indicates whether the device supports haptic feedback --- (Boolean)supportsHaptic; -- --@end -diff --git a/node_modules/react-native-haptic-feedback/package.json b/node_modules/react-native-haptic-feedback/package.json -index 86dfaa4..9cec8e4 100644 ---- a/node_modules/react-native-haptic-feedback/package.json -+++ b/node_modules/react-native-haptic-feedback/package.json -@@ -6,18 +6,7 @@ - "source": "src/index.ts", - "main": "./lib/commonjs/index.js", - "module": "./lib/module/index.js", -- "exports": { -- ".": { -- "import": { -- "types": "./lib/typescript/module/src/index.d.ts", -- "default": "./lib/module/index.js" -- }, -- "require": { -- "types": "./lib/typescript/commonjs/src/index.d.ts", -- "default": "./lib/commonjs/index.js" -- } -- } -- }, -+ "types": "./lib/typescript/module/src/index.d.ts", - "scripts": { - "typecheck": "tsc --noEmit --project tsconfig.test.json", - "test": "jest", diff --git a/src/CONST.ts b/src/CONST.ts index 9ee9ec4d9147..4ca9b45f13df 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -171,7 +171,7 @@ const CONST = { }, // Note: Group and Self-DM excluded as these are not tied to a Workspace - WORKSPACE_ROOM_TYPES: [chatTypes.POLICY_ADMINS, chatTypes.POLICY_ANNOUNCE, chatTypes.DOMAIN_ALL, chatTypes.POLICY_ROOM, chatTypes.POLICY_EXPENSE_CHAT], + WORKSPACE_ROOM_TYPES: [chatTypes.POLICY_ADMINS, chatTypes.POLICY_ANNOUNCE, chatTypes.DOMAIN_ALL, chatTypes.POLICY_ROOM, chatTypes.POLICY_EXPENSE_CHAT, chatTypes.INVOICE], ANDROID_PACKAGE_NAME, WORKSPACE_ENABLE_FEATURE_REDIRECT_DELAY: 100, ANIMATED_HIGHLIGHT_ENTRY_DELAY: 50, @@ -719,7 +719,9 @@ const CONST = { PRICING: `https://www.expensify.com/pricing`, COMPANY_CARDS_HELP: 'https://help.expensify.com/articles/expensify-classic/connect-credit-cards/company-cards/Commercial-Card-Feeds', CUSTOM_REPORT_NAME_HELP_URL: 'https://help.expensify.com/articles/expensify-classic/spending-insights/Custom-Templates', + CONFIGURE_REIMBURSEMENT_SETTINGS_HELP_URL: 'https://help.expensify.com/articles/expensify-classic/workspaces/Configure-Reimbursement-Settings', COPILOT_HELP_URL: 'https://help.expensify.com/articles/expensify-classic/copilots-and-delegates/Assign-or-remove-a-Copilot', + DELAYED_SUBMISSION_HELP_URL: 'https://help.expensify.com/articles/expensify-classic/reports/Automatically-submit-employee-reports', // Use Environment.getEnvironmentURL to get the complete URL with port number DEV_NEW_EXPENSIFY_URL: 'https://dev.new.expensify.com:', OLDDOT_URLS: { @@ -1009,6 +1011,7 @@ const CONST = { MAX_PREVIEW_AVATARS: 4, MAX_ROOM_NAME_LENGTH: 99, LAST_MESSAGE_TEXT_MAX_LENGTH: 200, + MIN_LENGTH_LAST_MESSAGE_WITH_ELLIPSIS: 20, OWNER_EMAIL_FAKE: '__FAKE__', OWNER_ACCOUNT_ID_FAKE: 0, DEFAULT_REPORT_NAME: 'Chat Report', @@ -2097,6 +2100,9 @@ const CONST = { ACCESS_VARIANTS: { CREATE: 'create', }, + PAGE_INDEX: { + CONFIRM: 'confirm', + }, PAYMENT_SELECTED: { BBA: 'BBA', PBA: 'PBA', @@ -2180,7 +2186,7 @@ const CONST = { AUTO_REIMBURSEMENT_MAX_LIMIT_CENTS: 2000000, AUTO_REIMBURSEMENT_DEFAULT_LIMIT_CENTS: 10000, AUTO_APPROVE_REPORTS_UNDER_DEFAULT_CENTS: 10000, - RANDOM_AUDIT_DEFAULT_PERCENTAGE: 5, + RANDOM_AUDIT_DEFAULT_PERCENTAGE: 0.05, AUTO_REPORTING_FREQUENCIES: { INSTANT: 'instant', @@ -4556,7 +4562,7 @@ const CONST = { { type: 'setupTags', autoCompleted: false, - title: 'Set up tags (optional)', + title: 'Set up tags', description: ({workspaceMoreFeaturesLink}) => 'Tags can be used if you want more details with every expense. Use tags for projects, clients, locations, departments, and more. If you need multiple levels of tags you can upgrade to a control plan.\n' + '\n' + diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 7fcb675dc191..cb8bf2fdb5d3 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -849,7 +849,7 @@ type OnyxValuesMapping = { // ONYXKEYS.NVP_TRYNEWDOT is HybridApp onboarding data [ONYXKEYS.NVP_TRYNEWDOT]: OnyxTypes.TryNewDot; - [ONYXKEYS.SAVED_SEARCHES]: OnyxTypes.SaveSearch[]; + [ONYXKEYS.SAVED_SEARCHES]: OnyxTypes.SaveSearch; [ONYXKEYS.RECENTLY_USED_CURRENCIES]: string[]; [ONYXKEYS.ACTIVE_CLIENTS]: string[]; [ONYXKEYS.DEVICE_ID]: string; diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 3d24e3a2dafc..dfcb42d3c4fe 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -35,7 +35,7 @@ const ROUTES = { SEARCH_CENTRAL_PANE: { route: 'search', - getRoute: ({query}: {query: SearchQueryString}) => `search?q=${encodeURIComponent(query)}` as const, + getRoute: ({query, name}: {query: SearchQueryString; name?: string}) => `search?q=${encodeURIComponent(query)}${name ? `&name=${name}` : ''}` as const, }, SEARCH_SAVED_SEARCH_RENAME: { route: 'search/saved-search/rename', @@ -72,7 +72,7 @@ const ROUTES = { SUBMIT_EXPENSE: 'submit-expense', FLAG_COMMENT: { route: 'flag/:reportID/:reportActionID', - getRoute: (reportID: string, reportActionID: string) => `flag/${reportID}/${reportActionID}` as const, + getRoute: (reportID: string, reportActionID: string, backTo?: string) => getUrlWithBackToParam(`flag/${reportID}/${reportActionID}` as const, backTo), }, CHAT_FINDER: 'chat-finder', PROFILE: { @@ -285,11 +285,12 @@ const ROUTES = { }, EDIT_REPORT_FIELD_REQUEST: { route: 'r/:reportID/edit/policyField/:policyID/:fieldID', - getRoute: (reportID: string, policyID: string, fieldID: string) => `r/${reportID}/edit/policyField/${policyID}/${fieldID}` as const, + getRoute: (reportID: string, policyID: string, fieldID: string, backTo?: string) => + getUrlWithBackToParam(`r/${reportID}/edit/policyField/${policyID}/${encodeURIComponent(fieldID)}` as const, backTo), }, REPORT_WITH_ID_DETAILS_SHARE_CODE: { route: 'r/:reportID/details/shareCode', - getRoute: (reportID: string) => `r/${reportID}/details/shareCode` as const, + getRoute: (reportID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/details/shareCode` as const, backTo), }, ATTACHMENTS: { route: 'attachment', @@ -298,19 +299,19 @@ const ROUTES = { }, REPORT_PARTICIPANTS: { route: 'r/:reportID/participants', - getRoute: (reportID: string) => `r/${reportID}/participants` as const, + getRoute: (reportID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/participants` as const, backTo), }, REPORT_PARTICIPANTS_INVITE: { route: 'r/:reportID/participants/invite', - getRoute: (reportID: string) => `r/${reportID}/participants/invite` as const, + getRoute: (reportID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/participants/invite` as const, backTo), }, REPORT_PARTICIPANTS_DETAILS: { route: 'r/:reportID/participants/:accountID', - getRoute: (reportID: string, accountID: number) => `r/${reportID}/participants/${accountID}` as const, + getRoute: (reportID: string, accountID: number, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/participants/${accountID}` as const, backTo), }, REPORT_PARTICIPANTS_ROLE_SELECTION: { route: 'r/:reportID/participants/:accountID/role', - getRoute: (reportID: string, accountID: number) => `r/${reportID}/participants/${accountID}/role` as const, + getRoute: (reportID: string, accountID: number, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/participants/${accountID}/role` as const, backTo), }, REPORT_WITH_ID_DETAILS: { route: 'r/:reportID/details', @@ -318,71 +319,75 @@ const ROUTES = { }, REPORT_WITH_ID_DETAILS_EXPORT: { route: 'r/:reportID/details/export/:connectionName', - getRoute: (reportID: string, connectionName: ConnectionName) => `r/${reportID}/details/export/${connectionName}` as const, + getRoute: (reportID: string, connectionName: ConnectionName, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/details/export/${connectionName}` as const, backTo), }, REPORT_SETTINGS: { route: 'r/:reportID/settings', - getRoute: (reportID: string) => `r/${reportID}/settings` as const, + getRoute: (reportID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/settings` as const, backTo), }, REPORT_SETTINGS_NAME: { route: 'r/:reportID/settings/name', - getRoute: (reportID: string) => `r/${reportID}/settings/name` as const, + getRoute: (reportID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/settings/name` as const, backTo), }, REPORT_SETTINGS_NOTIFICATION_PREFERENCES: { route: 'r/:reportID/settings/notification-preferences', - getRoute: (reportID: string) => `r/${reportID}/settings/notification-preferences` as const, + getRoute: (reportID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/settings/notification-preferences` as const, backTo), }, REPORT_SETTINGS_WRITE_CAPABILITY: { route: 'r/:reportID/settings/who-can-post', - getRoute: (reportID: string) => `r/${reportID}/settings/who-can-post` as const, + getRoute: (reportID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/settings/who-can-post` as const, backTo), }, REPORT_SETTINGS_VISIBILITY: { route: 'r/:reportID/settings/visibility', - getRoute: (reportID: string) => `r/${reportID}/settings/visibility` as const, + getRoute: (reportID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/settings/visibility` as const, backTo), }, SPLIT_BILL_DETAILS: { route: 'r/:reportID/split/:reportActionID', - getRoute: (reportID: string, reportActionID: string) => `r/${reportID}/split/${reportActionID}` as const, + getRoute: (reportID: string, reportActionID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/split/${reportActionID}` as const, backTo), }, TASK_TITLE: { route: 'r/:reportID/title', - getRoute: (reportID: string) => `r/${reportID}/title` as const, + getRoute: (reportID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/title` as const, backTo), }, REPORT_DESCRIPTION: { route: 'r/:reportID/description', - getRoute: (reportID: string) => `r/${reportID}/description` as const, + getRoute: (reportID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/description` as const, backTo), }, TASK_ASSIGNEE: { route: 'r/:reportID/assignee', - getRoute: (reportID: string) => `r/${reportID}/assignee` as const, + getRoute: (reportID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/assignee` as const, backTo), }, PRIVATE_NOTES_LIST: { route: 'r/:reportID/notes', - getRoute: (reportID: string) => `r/${reportID}/notes` as const, + getRoute: (reportID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/notes` as const, backTo), }, PRIVATE_NOTES_EDIT: { route: 'r/:reportID/notes/:accountID/edit', - getRoute: (reportID: string, accountID: string | number) => `r/${reportID}/notes/${accountID}/edit` as const, + getRoute: (reportID: string, accountID: string | number, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/notes/${accountID}/edit` as const, backTo), }, ROOM_MEMBERS: { route: 'r/:reportID/members', - getRoute: (reportID: string) => `r/${reportID}/members` as const, + getRoute: (reportID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/members` as const, backTo), }, ROOM_MEMBER_DETAILS: { route: 'r/:reportID/members/:accountID', - getRoute: (reportID: string, accountID: string | number) => `r/${reportID}/members/${accountID}` as const, + getRoute: (reportID: string, accountID: string | number, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/members/${accountID}` as const, backTo), }, ROOM_INVITE: { route: 'r/:reportID/invite/:role?', - getRoute: (reportID: string, role?: string) => { + getRoute: (reportID: string, role?: string, backTo?: string) => { const route = role ? (`r/${reportID}/invite/${role}` as const) : (`r/${reportID}/invite` as const); - return route; + return getUrlWithBackToParam(route, backTo); }, }, MONEY_REQUEST_HOLD_REASON: { - route: ':type/edit/reason/:transactionID?', - getRoute: (type: ValueOf, transactionID: string, reportID: string, backTo: string) => - `${type}/edit/reason/${transactionID}?backTo=${backTo}&reportID=${reportID}` as const, + route: ':type/edit/reason/:transactionID?/:searchHash?', + getRoute: (type: ValueOf, transactionID: string, reportID: string, backTo: string, searchHash?: number) => { + const route = searchHash + ? (`${type}/edit/reason/${transactionID}/${searchHash}/?backTo=${backTo}&reportID=${reportID}` as const) + : (`${type}/edit/reason/${transactionID}/?backTo=${backTo}&reportID=${reportID}` as const); + return route; + }, }, MONEY_REQUEST_CREATE: { route: ':action/:iouType/start/:transactionID/:reportID', @@ -404,9 +409,9 @@ const ROUTES = { `${action as string}/${iouType as string}/confirmation/${transactionID}/${reportID}${participantsAutoAssigned ? '?participantsAutoAssigned=true' : ''}` as const, }, MONEY_REQUEST_STEP_AMOUNT: { - route: ':action/:iouType/amount/:transactionID/:reportID', - getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`${action as string}/${iouType as string}/amount/${transactionID}/${reportID}`, backTo), + route: ':action/:iouType/amount/:transactionID/:reportID/:pageIndex?', + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, pageIndex: string, backTo = '') => + getUrlWithBackToParam(`${action as string}/${iouType as string}/amount/${transactionID}/${reportID}/${pageIndex}`, backTo), }, MONEY_REQUEST_STEP_TAX_RATE: { route: ':action/:iouType/taxRate/:transactionID/:reportID?', @@ -493,6 +498,10 @@ const ROUTES = { getRoute: (action: IOUAction, iouType: IOUType, orderWeight: number, transactionID: string, reportID: string, backTo = '', reportActionID?: string) => getUrlWithBackToParam(`${action as string}/${iouType as string}/tag/${orderWeight}/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}`, backTo), }, + SETTINGS_TAGS_ROOT: { + route: 'settings/:policyID/tags', + getRoute: (policyID: string, backTo = '') => getUrlWithBackToParam(`settings/${policyID}/tags`, backTo), + }, MONEY_REQUEST_STEP_WAYPOINT: { route: ':action/:iouType/waypoint/:transactionID/:reportID/:pageIndex', getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID?: string, pageIndex = '', backTo = '') => @@ -534,12 +543,27 @@ const ROUTES = { IOU_SEND_ADD_DEBIT_CARD: 'pay/new/add-debit-card', IOU_SEND_ENABLE_PAYMENTS: 'pay/new/enable-payments', - NEW_TASK: 'new/task', - NEW_TASK_ASSIGNEE: 'new/task/assignee', + NEW_TASK: { + route: 'new/task', + getRoute: (backTo?: string) => getUrlWithBackToParam('new/task', backTo), + }, + NEW_TASK_ASSIGNEE: { + route: 'new/task/assignee', + getRoute: (backTo?: string) => getUrlWithBackToParam('new/task/assignee', backTo), + }, NEW_TASK_SHARE_DESTINATION: 'new/task/share-destination', - NEW_TASK_DETAILS: 'new/task/details', - NEW_TASK_TITLE: 'new/task/title', - NEW_TASK_DESCRIPTION: 'new/task/description', + NEW_TASK_DETAILS: { + route: 'new/task/details', + getRoute: (backTo?: string) => getUrlWithBackToParam('new/task/details', backTo), + }, + NEW_TASK_TITLE: { + route: 'new/task/title', + getRoute: (backTo?: string) => getUrlWithBackToParam('new/task/title', backTo), + }, + NEW_TASK_DESCRIPTION: { + route: 'new/task/description', + getRoute: (backTo?: string) => getUrlWithBackToParam('new/task/description', backTo), + }, TEACHERS_UNITE: 'settings/teachersunite', I_KNOW_A_TEACHER: 'settings/teachersunite/i-know-a-teacher', @@ -1095,7 +1119,10 @@ const ROUTES = { route: 'referral/:contentType', getRoute: (contentType: string, backTo?: string) => getUrlWithBackToParam(`referral/${contentType}`, backTo), }, - PROCESS_MONEY_REQUEST_HOLD: 'hold-expense-educational', + PROCESS_MONEY_REQUEST_HOLD: { + route: 'hold-expense-educational', + getRoute: (backTo?: string) => getUrlWithBackToParam('hold-expense-educational', backTo), + }, TRAVEL_MY_TRIPS: 'travel', TRAVEL_TCS: 'travel/terms', TRACK_TRAINING_MODAL: 'track-training', @@ -1124,39 +1151,39 @@ const ROUTES = { }, TRANSACTION_DUPLICATE_REVIEW_PAGE: { route: 'r/:threadReportID/duplicates/review', - getRoute: (threadReportID: string) => `r/${threadReportID}/duplicates/review` as const, + getRoute: (threadReportID: string, backTo?: string) => getUrlWithBackToParam(`r/${threadReportID}/duplicates/review` as const, backTo), }, TRANSACTION_DUPLICATE_REVIEW_MERCHANT_PAGE: { route: 'r/:threadReportID/duplicates/review/merchant', - getRoute: (threadReportID: string) => `r/${threadReportID}/duplicates/review/merchant` as const, + getRoute: (threadReportID: string, backTo?: string) => getUrlWithBackToParam(`r/${threadReportID}/duplicates/review/merchant` as const, backTo), }, TRANSACTION_DUPLICATE_REVIEW_CATEGORY_PAGE: { route: 'r/:threadReportID/duplicates/review/category', - getRoute: (threadReportID: string) => `r/${threadReportID}/duplicates/review/category` as const, + getRoute: (threadReportID: string, backTo?: string) => getUrlWithBackToParam(`r/${threadReportID}/duplicates/review/category` as const, backTo), }, TRANSACTION_DUPLICATE_REVIEW_TAG_PAGE: { route: 'r/:threadReportID/duplicates/review/tag', - getRoute: (threadReportID: string) => `r/${threadReportID}/duplicates/review/tag` as const, + getRoute: (threadReportID: string, backTo?: string) => getUrlWithBackToParam(`r/${threadReportID}/duplicates/review/tag` as const, backTo), }, TRANSACTION_DUPLICATE_REVIEW_TAX_CODE_PAGE: { route: 'r/:threadReportID/duplicates/review/tax-code', - getRoute: (threadReportID: string) => `r/${threadReportID}/duplicates/review/tax-code` as const, + getRoute: (threadReportID: string, backTo?: string) => getUrlWithBackToParam(`r/${threadReportID}/duplicates/review/tax-code` as const, backTo), }, TRANSACTION_DUPLICATE_REVIEW_DESCRIPTION_PAGE: { route: 'r/:threadReportID/duplicates/review/description', - getRoute: (threadReportID: string) => `r/${threadReportID}/duplicates/review/description` as const, + getRoute: (threadReportID: string, backTo?: string) => getUrlWithBackToParam(`r/${threadReportID}/duplicates/review/description` as const, backTo), }, TRANSACTION_DUPLICATE_REVIEW_REIMBURSABLE_PAGE: { route: 'r/:threadReportID/duplicates/review/reimbursable', - getRoute: (threadReportID: string) => `r/${threadReportID}/duplicates/review/reimbursable` as const, + getRoute: (threadReportID: string, backTo?: string) => getUrlWithBackToParam(`r/${threadReportID}/duplicates/review/reimbursable` as const, backTo), }, TRANSACTION_DUPLICATE_REVIEW_BILLABLE_PAGE: { route: 'r/:threadReportID/duplicates/review/billable', - getRoute: (threadReportID: string) => `r/${threadReportID}/duplicates/review/billable` as const, + getRoute: (threadReportID: string, backTo?: string) => getUrlWithBackToParam(`r/${threadReportID}/duplicates/review/billable` as const, backTo), }, TRANSACTION_DUPLICATE_CONFIRMATION_PAGE: { route: 'r/:threadReportID/duplicates/confirm', - getRoute: (threadReportID: string) => `r/${threadReportID}/duplicates/confirm` as const, + getRoute: (threadReportID: string, backTo?: string) => getUrlWithBackToParam(`r/${threadReportID}/duplicates/confirm` as const, backTo), }, POLICY_ACCOUNTING_XERO_IMPORT: { route: 'settings/workspaces/:policyID/accounting/xero/import', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 920bd48dd42e..395f1c4d5fb1 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -244,6 +244,8 @@ const SCREENS = { SETTINGS_CATEGORIES_ROOT: 'Settings_Categories', }, + SETTINGS_TAGS_ROOT: 'Settings_Tags', + REPORT_SETTINGS: { ROOT: 'Report_Settings_Root', NAME: 'Report_Settings_Name', diff --git a/src/components/AccountSwitcher.tsx b/src/components/AccountSwitcher.tsx index 71970b88eac9..8d3e311c7c61 100644 --- a/src/components/AccountSwitcher.tsx +++ b/src/components/AccountSwitcher.tsx @@ -108,7 +108,7 @@ function AccountSwitcher() { const error = ErrorUtils.getLatestErrorField({errorFields}, 'connect'); const personalDetails = PersonalDetailsUtils.getPersonalDetailByEmail(email); return createBaseMenuItem(personalDetails, error, { - badgeText: translate('delegate.role', role), + badgeText: translate('delegate.role', {role}), onPress: () => { if (isOffline) { Modal.close(() => setShouldShowOfflineModal(true)); diff --git a/src/components/AccountSwitcherSkeletonView/index.tsx b/src/components/AccountSwitcherSkeletonView/index.tsx index 3faf7e563f3c..379a4094e032 100644 --- a/src/components/AccountSwitcherSkeletonView/index.tsx +++ b/src/components/AccountSwitcherSkeletonView/index.tsx @@ -22,7 +22,7 @@ function AccountSwitcherSkeletonView({shouldAnimate = true, avatarSize = CONST.A const StyleUtils = useStyleUtils(); const avatarPlaceholderSize = StyleUtils.getAvatarSize(avatarSize); const avatarPlaceholderRadius = avatarPlaceholderSize / 2; - const startPositionX = 30; + const startPositionX = avatarPlaceholderRadius; return ( diff --git a/src/components/AccountingConnectionConfirmationModal.tsx b/src/components/AccountingConnectionConfirmationModal.tsx index c472f215b6df..bfacd8c0bf76 100644 --- a/src/components/AccountingConnectionConfirmationModal.tsx +++ b/src/components/AccountingConnectionConfirmationModal.tsx @@ -14,11 +14,11 @@ function AccountingConnectionConfirmationModal({integrationToConnect, onCancel, return ( { + ReportUtils.navigateToDetailsPage(report, Navigation.getReportRHPActiveRoute()); + }, [report]); + const showActorDetails = useCallback(() => { // We should navigate to the details page if the report is a IOU/expense report if (shouldEnableDetailPageNavigation) { - return ReportUtils.navigateToDetailsPage(report); + goToDetailsPage(); + return; } if (ReportUtils.isExpenseReport(report) && report?.ownerAccountID) { @@ -107,7 +112,7 @@ function AvatarWithDisplayName({ // Report detail route is added as fallback but based on the current implementation this route won't be executed Navigation.navigate(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(report.reportID)); } - }, [report, shouldEnableDetailPageNavigation]); + }, [report, shouldEnableDetailPageNavigation, goToDetailsPage]); const headerView = ( @@ -172,7 +177,7 @@ function AvatarWithDisplayName({ return ( ReportUtils.navigateToDetailsPage(report)} + onPress={goToDetailsPage} style={[styles.flexRow, styles.alignItemsCenter, styles.flex1]} accessibilityLabel={title} role={CONST.ROLE.BUTTON} diff --git a/src/components/Button/validateSubmitShortcut/index.ts b/src/components/Button/validateSubmitShortcut/index.ts index f8cea44f73d6..29ba071c25f2 100644 --- a/src/components/Button/validateSubmitShortcut/index.ts +++ b/src/components/Button/validateSubmitShortcut/index.ts @@ -11,7 +11,7 @@ import type ValidateSubmitShortcut from './types'; const validateSubmitShortcut: ValidateSubmitShortcut = (isDisabled, isLoading, event) => { const eventTarget = event?.target as HTMLElement; - if (isDisabled || isLoading || eventTarget.nodeName === 'TEXTAREA') { + if (isDisabled || isLoading || eventTarget.nodeName === 'TEXTAREA' || (eventTarget?.contentEditable === 'true' && eventTarget.ariaMultiLine)) { return false; } diff --git a/src/components/EmptyStateComponent/index.tsx b/src/components/EmptyStateComponent/index.tsx index 876f1a745403..8eb991b17b63 100644 --- a/src/components/EmptyStateComponent/index.tsx +++ b/src/components/EmptyStateComponent/index.tsx @@ -22,6 +22,7 @@ function EmptyStateComponent({ buttonAction, containerStyles, title, + titleStyles, subtitle, headerStyles, headerContentStyles, @@ -30,7 +31,7 @@ function EmptyStateComponent({ }: EmptyStateComponentProps) { const styles = useThemeStyles(); const [videoAspectRatio, setVideoAspectRatio] = useState(VIDEO_ASPECT_RATIO); - const {isSmallScreenWidth} = useResponsiveLayout(); + const {shouldUseNarrowLayout} = useResponsiveLayout(); const setAspectRatio = (event: VideoReadyForDisplayEvent | VideoLoadedEventType | undefined) => { if (!event) { @@ -82,7 +83,10 @@ function EmptyStateComponent({ }, [headerMedia, headerMediaType, headerContentStyles, videoAspectRatio, styles.emptyStateVideo, lottieWebViewStyles]); return ( - + {HeaderComponent} - - {title} - {subtitle} + + {title} + {typeof subtitle === 'string' ? {subtitle} : subtitle} {!!buttonText && !!buttonAction && (