diff --git a/.github/scripts/validateActionsAndWorkflows.sh b/.github/scripts/validateActionsAndWorkflows.sh index 83fa126a384c..8d186ea51a44 100755 --- a/.github/scripts/validateActionsAndWorkflows.sh +++ b/.github/scripts/validateActionsAndWorkflows.sh @@ -43,9 +43,7 @@ for ((i=0; i < ${#WORKFLOWS[@]}; i++)); do WORKFLOW=${WORKFLOWS[$i]} # Skip linting e2e workflow due to bug here: https://github.com/SchemaStore/schemastore/issues/2579 - if [[ "$WORKFLOW" == './workflows/e2ePerformanceTests.yml' - || "$WORKFLOW" == './workflows/platformDeploy.yml' - || "$WORKFLOW" == './workflows/testBuild.yml' ]]; then + if [[ "$WORKFLOW" == './workflows/e2ePerformanceTests.yml' ]]; then continue fi diff --git a/android/app/build.gradle b/android/app/build.gradle index 35bd66d07a16..6123973ee4f4 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -106,8 +106,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001032102 - versionName "1.3.21-2" + versionCode 1001032303 + versionName "1.3.23-3" } splits { diff --git a/contributingGuides/CONTRIBUTING.md b/contributingGuides/CONTRIBUTING.md index fb256bf7e955..a6b9a3404ac4 100644 --- a/contributingGuides/CONTRIBUTING.md +++ b/contributingGuides/CONTRIBUTING.md @@ -43,9 +43,9 @@ Note: if you are hired for an Upwork job and have any job-specific questions, pl If you've found a vulnerability, please email security@expensify.com with the subject `Vulnerability Report` instead of creating an issue. ## Payment for Contributions -We hire and pay external contributors via Upwork.com. If you'd like to be paid for contributing, please create an Upwork account, apply for an available job in [GitHub](https://github.com/Expensify/App/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22), and finally apply for the job in Upwork once your proposal gets selected in GitHub. If you think your compensation should be increased for a specific job, you can request a reevaluation by commenting in the Github issue where the Upwork job was posted. +We hire and pay external contributors via Upwork.com. If you'd like to be paid for contributing or reporting a bug, please create an Upwork account, apply for an available job in [GitHub](https://github.com/Expensify/App/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22), and finally apply for the job in Upwork once your proposal gets selected in GitHub. If you think your compensation should be increased for a specific job, you can request a reevaluation by commenting in the Github issue where the Upwork job was posted. -Payment for your contributions will be made no less than 7 days after the pull request is deployed to production to allow for regression testing. If a regression occurs, payment will be issued 7 days after all regressions are fixed. If you have not received payment after 8 days of the PR being deployed to production and there being no regressions, please email contributors@expensify.com referencing the GH issue and your GH handle. +Payment for your contributions and bug reports will be made no less than 7 days after the pull request is deployed to production to allow for regression testing. If a regression occurs, payment will be issued 7 days after all regressions are fixed. If you have not received payment after 8 days of the PR being deployed to production and there being no regressions, please email contributors@expensify.com referencing the GH issue and your GH handle. New contributors are limited to working on one job at a time, however experienced contributors may work on numerous jobs simultaneously. @@ -67,21 +67,21 @@ A job could be fixing a bug or working on a new feature. There are two ways you #### Finding a job that Expensify posted This is the most common scenario for contributors. The Expensify team posts new jobs to the Upwork job list [here](https://www.upwork.com/ab/jobs/search/?q=Expensify%20React%20Native&sort=recency&user_location_match=2) (you must be signed in to Upwork to view jobs). Each job in Upwork has a corresponding GitHub issue, which will include instructions to follow. You can also view all open jobs in the Expensify/App GH repository by searching for GH issues with the [`Help Wanted` label](https://github.com/Expensify/App/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22). Lastly, you can follow the [@ExpensifyOSS](https://twitter.com/ExpensifyOSS) Twitter account to see a live feed of jobs that are posted. -#### Proposing a job that Expensify hasn't posted -It’s possible that you found a new bug that we haven’t posted as a job to the [GitHub repository](https://github.com/Expensify/App/issues?q=is%3Aissue). This is an opportunity to propose a job. If it's a valid job proposal that we choose to implement by deploying it to production — either internally or via an external contributor — then we will compensate you $250 for identifying and proposing the bug (we do not compensate for reporting new feature requests). If the bug is fixed by a PR that is not associated with your bug report, then you will not be eligible for the corresponding compensation unless you can find the PR that fixed it and prove your bug report came first. +#### Raising jobs and bugs +It’s possible that you found a new bug that we haven’t posted as a job to the [GitHub repository](https://github.com/Expensify/App/issues?q=is%3Aissue). This is an opportunity to raise it and claim the bug bounty. If it's a valid bug that we choose to resolve by deploying it to production — either internally or via an external contributor — then we will compensate you $250 for identifying the bug (we do not compensate for reporting new feature requests). If the bug is fixed by a PR that is not associated with your bug report, then you will not be eligible for the corresponding compensation unless you can find the PR that fixed it and prove your bug report came first. - Note: If you get assigned the job you proposed **and** you complete the job, this $250 for identifying the improvement is *in addition to* the reward you will be paid for completing the job. - Note about proposed bugs: Expensify has the right not to pay the $250 reward if the suggested bug has already been reported. Following, if more than one contributor proposes the same bug, the contributor who posted it first in the [#expensify-bugs](https://expensify.slack.com/archives/C049HHMV9SM) Slack channel is the one who is eligible for the bonus. - Note: whilst you may optionally propose a solution for that job on Slack, solutions are ultimately reviewed in GitHub. The onus is on you to propose the solution on GitHub, and/or ensure the issue creator will include a link to your proposal. -Please follow these steps to propose a job: +Please follow these steps to propose a job or raise a bug: 1. Check to ensure a GH issue does not already exist for this job in the [New Expensify Issue list](https://github.com/Expensify/App/issues). 2. Check to ensure the `Bug:` or `Feature Request:` was not already posted in Slack (specifically the #expensify-bugs or #expensify-open-source [Slack channels](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#slack-channels)). Use your best judgement by searching for similar titles and issue descriptions. 3. If your bug or new feature matches with an existing issue, please comment on that Slack thread or GitHub issue with your findings if you think it will help solve the issue. 4. If there is no existing GitHub issue or Upwork job, check if the issue is happening on prod (as opposed to only happening on dev) 5. If the issue is just in dev then it means it's a new issue and has not been deployed to production. In this case, you should try to find the offending PR and comment in the issue tied to the PR and ask the assigned users to add the `DeployBlockerCash` label. If you can't find it, follow the reporting instructions in the next item, but note that the issue is a regression only found in dev and not in prod. -6. If the issue happens in main, staging, or production then report the issue(s) in the #expensify-bugs Slack channel, prefixed with `Bug:` or `Feature Request:`. Please use the templates for bugs and feature requests that are bookmarked in the #expensify-bugs channel. View [this guide](https://github.com/Expensify/App/blob/main/contributingGuides/HOW_TO_CREATE_A_PLAN.md) for help creating a plan when proposing a feature request. - - **Important note/reminder**: never share any information pertaining to a customer of Expensify when describing the bug. This includes, and is not limited to, a customer's name, email, and contact information. +6. If the issue happens in main, staging, or production then report the issue(s) in the #expensify-bugs Slack channel, using the report bug workflow. You can do this by clicking 'Workflow > report Bug', or typing `/Report bug`. View [this guide](https://github.com/Expensify/App/blob/main/contributingGuides/HOW_TO_CREATE_A_PLAN.md) for help creating a plan when proposing a feature request. + - **Important note/reminder**: never share any information pertaining to a customer of Expensify when describing the bug. This includes, and is not limited to, a customer's name, email, and contact information. 7. The Expensify team will review your job proposal in the appropriate slack channel. If you've provided a quality proposal that we choose to implement, a GitHub issue will be created and your Slack handle will be included in the original post after `Issue reported by:` 8. If an external contributor other than yourself is hired to work on the issue, you will also be hired for the same job in Upwork to receive your payout. No additional work is required. If the issue is fixed internally, a dedicated job will be created to hire and pay you after the issue is fixed. 9. Payment will be made 7 days after code is deployed to production if there are no regressions. If a regression is discovered, payment will be issued 7 days after all regressions are fixed. @@ -181,9 +181,19 @@ Follow all the above above steps and processes. When you find a job you'd like t #### Guide on Acronyms used within Expensify Communication During communication with Expensify, you will come across a variety of acronyms used by our team. While acronyms can be useful, they cease to be the moment they are not known to the receiver. As such, we wanted to create a list here of our most commonly used acronyms and what they're referring to. Lastly, please never hesitate to ask in slack or the GH issue if there are any that are not understood/known! -- BZ: Bug Zero (Expensify internal team in charge of managing the GH issues related to our open-source project) -- LHN: Left Hand Navigation (Primary navigation modal in Expensify Chat, docked on the left hand side) -- OP: Original Post (Most commonly the post in E/App GH issues that reports the bug) -- GBR: Green Brick Road (UX Design Principle that utlizes green indicators on action items to encourage the user down the optimal path for a given process or task) -- VBA: Verified Bank Account (Bank account that has been verified as real and belonging to the correct business/individual) -- NAB: Not a Blocker (An issue that doesn't block progress, but would be nice to not have) +- **ND/NewDot:** new.expensify.com +- **OD/OldDot:** expensify.com +- **BZ:** Bug Zero (Expensify internal team in charge of managing the GH issues related to our open-source project) +- **LHN:** Left Hand Navigation (Primary navigation modal in Expensify Chat, docked on the left hand side) +- **OP:** Original Post (Most commonly the post in E/App GH issues that reports the bug) +- **GBR:** Green Brick Road (UX Design Principle that utlizes green indicators on action items to encourage the user down the optimal path for a given process or task) +- **RBR:** Red Brick Road (UX Design Principle that utlizes red indicators on action items to encourage the user down the optimal path for handling and discovering errors) +- **VBA:** Verified Bank Account (Bank account that has been verified as real and belonging to the correct business/individual) +- **NAB:** Not a Blocker (An issue that doesn't block progress, but would be nice to not have) +- **LHN:** Left Hand Navigation +- **IOU:** I owe you (used to describe payment requests between users) +- **OTP:** One time password, or magic sign-in +- **RHP:** Right Hand Panel (on larger screens, pages are often displayed docked to the right side of the screen) +- **QA:** Quality Assurance +- **GH:** GitHub +- **LGTM:*** Looks good to me \ No newline at end of file diff --git a/dangerfile.js b/dangerfile.js deleted file mode 100644 index 1fa05fde711f..000000000000 --- a/dangerfile.js +++ /dev/null @@ -1,7 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -import path from 'path'; -import {dangerReassure} from 'reassure'; - -dangerReassure({ - inputFilePath: path.join(__dirname, './.reassure/output.md'), -}); diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 49f15a3b12c0..60d60934c2ba 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -218,7 +218,11 @@ platform :ios do contact_phone: ENV["APPLE_CONTACT_PHONE"], demo_account_name: ENV["APPLE_DEMO_EMAIL"], demo_account_password: ENV["APPLE_DEMO_PASSWORD"], - notes: "Use the account provided. Thank you for the review." + notes: "1. Log into the Expensify app using the provided email + 2. Now, you have to log in to this gmail account on https://mail.google.com/ so you can retrieve a One-Time-Password + 3. To log in to the gmail account, use the password above (That's NOT a password for the Expensify app but for the Gmail account) + 4. At the Gmail inbox, you should have received a one-time 6 digit magic code + 5. Use that to sign in" } ) rescue Exception => e diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 7cae7f11858c..974272372c41 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.3.21 + 1.3.23 CFBundleSignature ???? CFBundleURLTypes @@ -30,7 +30,7 @@ CFBundleVersion - 1.3.21.2 + 1.3.23.3 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index efbfaf401e5b..c18b7cd53694 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.3.21 + 1.3.23 CFBundleSignature ???? CFBundleVersion - 1.3.21.2 + 1.3.23.3 diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 055d696ec5d8..6cb4235c58c8 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -16,7 +16,7 @@ PODS: - Airship/Core - Airship/PreferenceCenter (16.11.3): - Airship/Core - - AirshipFrameworkProxy (2.0.5): + - AirshipFrameworkProxy (2.0.8): - Airship (= 16.11.3) - Airship/MessageCenter (= 16.11.3) - Airship/PreferenceCenter (= 16.11.3) @@ -495,8 +495,8 @@ PODS: - React-jsinspector (0.71.2-alpha.3) - React-logger (0.71.2-alpha.3): - glog - - react-native-airship (15.2.3): - - AirshipFrameworkProxy (= 2.0.5) + - react-native-airship (15.2.6): + - AirshipFrameworkProxy (= 2.0.8) - React-Core - react-native-blob-util (0.17.3): - React-Core @@ -1018,8 +1018,8 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Airship: c70eed50e429f97f5adb285423c7291fb7a032ae - AirshipFrameworkProxy: 2eefb77bb77b5120b0f48814b0d44439aa3ad415 - boost: 57d2868c099736d80fcd648bf211b4431e51a558 + AirshipFrameworkProxy: 7bc4130c668c6c98e2d4c60fe4c9eb61a999be99 + boost: a7c83b31436843459a1961bfd74b96033dc77234 CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 FBLazyVector: ff54429f0110d3c722630a98096ba689c39f6d5f @@ -1062,7 +1062,7 @@ SPEC CHECKSUMS: Permission-LocationWhenInUse: 3ba99e45c852763f730eabecec2870c2382b7bd4 Plaid: 7d340abeadb46c7aa1a91f896c5b22395a31fcf2 PromisesObjC: 09985d6d70fbe7878040aa746d78236e6946d2ef - RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1 + RCT-Folly: 0080d0a6ebf2577475bda044aa59e2ca1f909cda RCTRequired: e9e7b8b45aa9bedb2fdad71740adf07a7265b9be RCTTypeSafety: 9ae0e9206625e995f0df4d5b9ddc94411929fb30 React: a71c8e1380f07e01de721ccd52bcf9c03e81867d @@ -1076,7 +1076,7 @@ SPEC CHECKSUMS: React-jsiexecutor: 6f986feb67cf66edff7f98090ca797a67d0a44fb React-jsinspector: 31517b1de3fadf93ad8558840a8974c7a7160bd3 React-logger: b90aa6ed0dbc30717dc72d843af3cf4550297b22 - react-native-airship: 25045092934bf6eabf483e803af0a6e31826b8b9 + react-native-airship: 5d19f4ba303481cf4101ff9dee9249ef6a8a6b64 react-native-blob-util: 99f4d79189252f597fe0d810c57a3733b1b1dea6 react-native-cameraroll: 8ffb0af7a5e5de225fd667610e2979fc1f0c2151 react-native-config: 7cd105e71d903104e8919261480858940a6b9c0e @@ -1135,4 +1135,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 4ed1c7b099741c82e2b0411b95f6468e72be6c76 -COCOAPODS: 1.12.1 +COCOAPODS: 1.12.0 diff --git a/package-lock.json b/package-lock.json index bcade802cd0b..f623b4e7717c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.3.21-2", + "version": "1.3.23-3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.3.21-2", + "version": "1.3.23-3", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -35,13 +35,13 @@ "@react-navigation/native": "6.0.13", "@react-navigation/stack": "6.3.1", "@react-ng/bounds-observer": "^0.2.1", - "@ua/react-native-airship": "^15.2.3", + "@ua/react-native-airship": "^15.2.6", "awesome-phonenumber": "^5.4.0", "babel-plugin-transform-remove-console": "^6.9.4", "babel-polyfill": "^6.26.0", "dom-serializer": "^0.2.2", "domhandler": "^4.3.0", - "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#68abe48ad71a98604fdbf5e8e960023ed5807ec2", + "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#c898563fe851d9a4d594fa9afbdd1ddab5971636", "fbjs": "^3.0.2", "html-entities": "^1.3.1", "htmlparser2": "^7.2.0", @@ -142,7 +142,6 @@ "concurrently": "^5.3.0", "copy-webpack-plugin": "^6.4.1", "css-loader": "^6.7.2", - "danger": "^11.2.6", "diff-so-fancy": "^1.3.0", "dotenv": "^16.0.3", "electron": "22.3.7", @@ -2820,70 +2819,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@gitbeaker/core": { - "version": "21.7.0", - "resolved": "https://registry.npmjs.org/@gitbeaker/core/-/core-21.7.0.tgz", - "integrity": "sha512-cw72rE7tA27wc6JJe1WqeAj9v/6w0S7XJcEji+bRNjTlUfE1zgfW0Gf1mbGUi7F37SOABGCosQLfg9Qe63aIqA==", - "dev": true, - "dependencies": { - "@gitbeaker/requester-utils": "^21.7.0", - "form-data": "^3.0.0", - "li": "^1.3.0", - "xcase": "^2.0.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@gitbeaker/node": { - "version": "21.7.0", - "resolved": "https://registry.npmjs.org/@gitbeaker/node/-/node-21.7.0.tgz", - "integrity": "sha512-OdM3VcTKYYqboOsnbiPcO0XimXXpYK4gTjARBZ6BWc+1LQXKmqo+OH6oUbyxOoaFu9hHECafIt3WZU3NM4sZTg==", - "deprecated": "Please use its successor @gitbeaker/rest", - "dev": true, - "dependencies": { - "@gitbeaker/core": "^21.7.0", - "@gitbeaker/requester-utils": "^21.7.0", - "form-data": "^3.0.0", - "got": "^11.1.4", - "xcase": "^2.0.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@gitbeaker/requester-utils": { - "version": "21.7.0", - "resolved": "https://registry.npmjs.org/@gitbeaker/requester-utils/-/requester-utils-21.7.0.tgz", - "integrity": "sha512-eLTaVXlBnh8Qimj6QuMMA06mu/mLcJm3dy8nqhhn/Vm/D25sPrvpGwmbfFyvzj6QujPqtHvFfsCHtyZddL01qA==", - "dev": true, - "dependencies": { - "form-data": "^3.0.0", - "query-string": "^6.12.1", - "xcase": "^2.0.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@gitbeaker/requester-utils/node_modules/query-string": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", - "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", - "dev": true, - "dependencies": { - "decode-uri-component": "^0.2.0", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@gorhom/portal": { "version": "1.0.14", "license": "MIT", @@ -4913,15 +4848,6 @@ "@octokit/core": ">=4" } }, - "node_modules/@octokit/plugin-request-log": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", - "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", - "dev": true, - "peerDependencies": { - "@octokit/core": ">=3" - } - }, "node_modules/@octokit/plugin-rest-endpoint-methods": { "version": "5.16.2", "dev": true, @@ -5004,101 +4930,6 @@ "@octokit/openapi-types": "^13.1.0" } }, - "node_modules/@octokit/rest": { - "version": "18.12.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz", - "integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==", - "dev": true, - "dependencies": { - "@octokit/core": "^3.5.1", - "@octokit/plugin-paginate-rest": "^2.16.8", - "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^5.12.0" - } - }, - "node_modules/@octokit/rest/node_modules/@octokit/auth-token": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", - "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", - "dev": true, - "dependencies": { - "@octokit/types": "^6.0.3" - } - }, - "node_modules/@octokit/rest/node_modules/@octokit/core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", - "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", - "dev": true, - "dependencies": { - "@octokit/auth-token": "^2.4.4", - "@octokit/graphql": "^4.5.8", - "@octokit/request": "^5.6.3", - "@octokit/request-error": "^2.0.5", - "@octokit/types": "^6.0.3", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/rest/node_modules/@octokit/endpoint": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", - "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", - "dev": true, - "dependencies": { - "@octokit/types": "^6.0.3", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/rest/node_modules/@octokit/graphql": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", - "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", - "dev": true, - "dependencies": { - "@octokit/request": "^5.6.0", - "@octokit/types": "^6.0.3", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/rest/node_modules/@octokit/plugin-paginate-rest": { - "version": "2.21.3", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz", - "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", - "dev": true, - "dependencies": { - "@octokit/types": "^6.40.0" - }, - "peerDependencies": { - "@octokit/core": ">=2" - } - }, - "node_modules/@octokit/rest/node_modules/@octokit/request": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", - "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", - "dev": true, - "dependencies": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.1.0", - "@octokit/types": "^6.16.1", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/rest/node_modules/@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", - "dev": true, - "dependencies": { - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, "node_modules/@octokit/types": { "version": "6.41.0", "dev": true, @@ -16066,9 +15897,9 @@ } }, "node_modules/@ua/react-native-airship": { - "version": "15.2.3", - "resolved": "https://registry.npmjs.org/@ua/react-native-airship/-/react-native-airship-15.2.3.tgz", - "integrity": "sha512-94fgHJcxc4qUy9OmBqt6VuuWggoeCMNIosz3/qwiUNsUVoE1deWcOEG9vULmXcTKX+O9Wal/mKsuPGiFCALTDQ==", + "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==", "engines": { "node": ">= 16.0.0" }, @@ -17187,24 +17018,6 @@ "version": "1.0.1", "license": "MIT" }, - "node_modules/async-retry": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.2.3.tgz", - "integrity": "sha512-tfDb02Th6CE6pJUF2gjW5ZVjsgwlucVXOEQMvEX9JgSJMs9gAX+Nz3xRuJBKuUYjTSYORqvDBORdAQ3LU59g7Q==", - "dev": true, - "dependencies": { - "retry": "0.12.0" - } - }, - "node_modules/async-retry/node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, "node_modules/asynckit": { "version": "0.4.0", "dev": true, @@ -18787,12 +18600,6 @@ "node": ">=0.4.0" } }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "dev": true - }, "node_modules/buffer-fill": { "version": "1.0.0", "dev": true, @@ -21011,92 +20818,6 @@ "dev": true, "license": "BSD-2-Clause" }, - "node_modules/danger": { - "version": "11.2.6", - "resolved": "https://registry.npmjs.org/danger/-/danger-11.2.6.tgz", - "integrity": "sha512-EEeuDmUcxPGJ166q7Zzz1WEiV+e0qbPopaX4sXxds8U5doGMdw/8oOUOVye7JiHIBuss3KvQWt4YHZeD3jSCfw==", - "dev": true, - "dependencies": { - "@gitbeaker/node": "^21.3.0", - "@octokit/rest": "^18.12.0", - "async-retry": "1.2.3", - "chalk": "^2.3.0", - "commander": "^2.18.0", - "core-js": "^3.8.2", - "debug": "^4.1.1", - "fast-json-patch": "^3.0.0-1", - "get-stdin": "^6.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.1", - "hyperlinker": "^1.0.0", - "json5": "^2.1.0", - "jsonpointer": "^5.0.0", - "jsonwebtoken": "^9.0.0", - "lodash.find": "^4.6.0", - "lodash.includes": "^4.3.0", - "lodash.isobject": "^3.0.2", - "lodash.keys": "^4.0.8", - "lodash.mapvalues": "^4.6.0", - "lodash.memoize": "^4.1.2", - "memfs-or-file-map-to-github-branch": "^1.2.1", - "micromatch": "^4.0.4", - "node-cleanup": "^2.1.2", - "node-fetch": "^2.6.7", - "override-require": "^1.1.1", - "p-limit": "^2.1.0", - "parse-diff": "^0.7.0", - "parse-git-config": "^2.0.3", - "parse-github-url": "^1.0.2", - "parse-link-header": "^2.0.0", - "pinpoint": "^1.1.0", - "prettyjson": "^1.2.1", - "readline-sync": "^1.4.9", - "regenerator-runtime": "^0.13.9", - "require-from-string": "^2.0.2", - "supports-hyperlinks": "^1.0.1" - }, - "bin": { - "danger": "distribution/commands/danger.js", - "danger-ci": "distribution/commands/danger-ci.js", - "danger-init": "distribution/commands/danger-init.js", - "danger-js": "distribution/commands/danger.js", - "danger-local": "distribution/commands/danger-local.js", - "danger-pr": "distribution/commands/danger-pr.js", - "danger-process": "distribution/commands/danger-process.js", - "danger-reset-status": "distribution/commands/danger-reset-status.js", - "danger-runner": "distribution/commands/danger-runner.js" - }, - "engines": { - "node": ">=14.13.1" - } - }, - "node_modules/danger/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/danger/node_modules/fast-json-patch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz", - "integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==", - "dev": true - }, - "node_modules/danger/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/data-urls": { "version": "3.0.2", "dev": true, @@ -21809,15 +21530,6 @@ "stream-shift": "^1.0.0" } }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, "node_modules/ee-first": { "version": "1.1.1", "license": "MIT" @@ -23972,18 +23684,6 @@ "version": "2.0.0", "license": "MIT" }, - "node_modules/expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", - "dev": true, - "dependencies": { - "homedir-polyfill": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/expect": { "version": "29.4.1", "license": "MIT", @@ -24102,8 +23802,8 @@ }, "node_modules/expensify-common": { "version": "1.0.0", - "resolved": "git+ssh://git@github.com/Expensify/expensify-common.git#68abe48ad71a98604fdbf5e8e960023ed5807ec2", - "integrity": "sha512-OhM2d7W++k0RVscwBpl0cvKXp3aZmflVawQ9Ebh2RBTdy98VZUwq2iZyCDISGNyETthqqsRq8vxy2lFQZZ19RA==", + "resolved": "git+ssh://git@github.com/Expensify/expensify-common.git#c898563fe851d9a4d594fa9afbdd1ddab5971636", + "integrity": "sha512-WjxHYpqebNsPKJC+SBhgsYNSib+8LptZv/BKt8hc67psJjO9JdrTpAHuoZ0n1lCTQ2DhpDERjqTsQbpUqWbgIg==", "license": "MIT", "dependencies": { "classnames": "2.3.1", @@ -25137,15 +24837,6 @@ "readable-stream": "^2.0.0" } }, - "node_modules/fs-exists-sync": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", - "integrity": "sha512-cR/vflFyPZtrN6b38ZyWxpWdhlXrzZEBawlpBQMq7033xVY7/kg0GDMBK5jg8lDYQckdJ5x/YC88lM3C7VMsLg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/fs-extra": { "version": "9.1.0", "license": "MIT", @@ -25332,41 +25023,6 @@ "node": ">=6" } }, - "node_modules/git-config-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/git-config-path/-/git-config-path-1.0.1.tgz", - "integrity": "sha512-KcJ2dlrrP5DbBnYIZ2nlikALfRhKzNSX0stvv3ImJ+fvC4hXKoV+U+74SV0upg+jlQZbrtQzc0bu6/Zh+7aQbg==", - "dev": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "fs-exists-sync": "^0.1.0", - "homedir-polyfill": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/git-config-path/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/git-config-path/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/github-slugger": { "version": "1.4.0", "dev": true, @@ -25935,18 +25591,6 @@ "react-is": "^16.7.0" } }, - "node_modules/homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dev": true, - "dependencies": { - "parse-passwd": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/hosted-git-info": { "version": "4.1.0", "dev": true, @@ -26501,15 +26145,6 @@ "which": "bin/which" } }, - "node_modules/hyperlinker": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hyperlinker/-/hyperlinker-1.0.0.tgz", - "integrity": "sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/hyphenate-style-name": { "version": "1.0.4", "license": "BSD-3-Clause" @@ -26730,12 +26365,6 @@ "version": "2.0.4", "license": "ISC" }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, "node_modules/inline-style-parser": { "version": "0.1.1", "dev": true, @@ -30839,31 +30468,6 @@ "version": "0.0.0", "license": "Public Domain" }, - "node_modules/jsonpointer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", - "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jsonwebtoken": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", - "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==", - "dev": true, - "dependencies": { - "jws": "^3.2.2", - "lodash": "^4.17.21", - "ms": "^2.1.1", - "semver": "^7.3.8" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, "node_modules/jsx-ast-utils": { "version": "3.3.3", "dev": true, @@ -30884,27 +30488,6 @@ "node": ">=8" } }, - "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "dev": true, - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "dev": true, - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, "node_modules/keyv": { "version": "4.5.2", "dev": true, @@ -31029,12 +30612,6 @@ "node": ">= 0.8.0" } }, - "node_modules/li": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/li/-/li-1.3.0.tgz", - "integrity": "sha512-z34TU6GlMram52Tss5mt1m//ifRIpKH5Dqm7yUVOdHI+BQCs9qGPHFaCUTIzsWX7edN30aa2WrPwR7IO10FHaw==", - "dev": true - }, "node_modules/lie": { "version": "3.1.1", "license": "MIT", @@ -31146,46 +30723,10 @@ "version": "4.0.8", "license": "MIT" }, - "node_modules/lodash.find": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz", - "integrity": "sha512-yaRZoAV3Xq28F1iafWN1+a0rflOej93l1DQUejs3SZ41h2O9UJBoS9aueGjPDgAl4B6tPC0NuuchLKaDQQ3Isg==", - "dev": true - }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", - "dev": true - }, "node_modules/lodash.isequal": { "version": "4.5.0", "license": "MIT" }, - "node_modules/lodash.isobject": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", - "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==", - "dev": true - }, - "node_modules/lodash.keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-4.2.0.tgz", - "integrity": "sha512-J79MkJcp7Df5mizHiVNpjoHXLi4HLjh9VLS/M7lQSGoQ+0oQ+lWEigREkqKyizPB1IawvQLLKY8mzEcm1tkyxQ==", - "dev": true - }, - "node_modules/lodash.mapvalues": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", - "integrity": "sha512-JPFqXFeZQ7BfS00H58kClY7SPVeHertPE0lNuCyZ26/XlN8TvakYD7b9bGyNmXbT/D3BbtPAAmq90gPWqLkxlQ==", - "dev": true - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, "node_modules/lodash.merge": { "version": "4.6.2", "dev": true, @@ -31817,15 +31358,6 @@ "node": ">= 4.0.0" } }, - "node_modules/memfs-or-file-map-to-github-branch": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/memfs-or-file-map-to-github-branch/-/memfs-or-file-map-to-github-branch-1.2.1.tgz", - "integrity": "sha512-I/hQzJ2a/pCGR8fkSQ9l5Yx+FQ4e7X6blNHyWBm2ojeFLT3GVzGkTj7xnyWpdclrr7Nq4dmx3xrvu70m3ypzAQ==", - "dev": true, - "dependencies": { - "@octokit/rest": "^16.43.0 || ^17.11.0 || ^18.12.0" - } - }, "node_modules/memoizerific": { "version": "1.11.3", "dev": true, @@ -33522,12 +33054,6 @@ "license": "MIT", "optional": true }, - "node_modules/node-cleanup": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", - "integrity": "sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==", - "dev": true - }, "node_modules/node-dir": { "version": "0.1.17", "license": "MIT", @@ -34267,12 +33793,6 @@ "node": ">=0.10.0" } }, - "node_modules/override-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/override-require/-/override-require-1.1.1.tgz", - "integrity": "sha512-eoJ9YWxFcXbrn2U8FKT6RV+/Kj7fiGAB1VvHzbYKt8xM5ZuKZgCGvnHzDxmreEjcBH28ejg5MiOH4iyY1mQnkg==", - "dev": true - }, "node_modules/p-all": { "version": "2.1.0", "dev": true, @@ -34463,12 +33983,6 @@ "safe-buffer": "^5.1.1" } }, - "node_modules/parse-diff": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/parse-diff/-/parse-diff-0.7.1.tgz", - "integrity": "sha512-1j3l8IKcy4yRK2W4o9EYvJLSzpAVwz4DXqCewYyx2vEwk2gcf3DBPqc8Fj4XV3K33OYJ08A8fWwyu/ykD/HUSg==", - "dev": true - }, "node_modules/parse-entities": { "version": "2.0.0", "dev": true, @@ -34486,32 +34000,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/parse-git-config": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/parse-git-config/-/parse-git-config-2.0.3.tgz", - "integrity": "sha512-Js7ueMZOVSZ3tP8C7E3KZiHv6QQl7lnJ+OkbxoaFazzSa2KyEHqApfGbU3XboUgUnq4ZuUmskUpYKTNx01fm5A==", - "dev": true, - "dependencies": { - "expand-tilde": "^2.0.2", - "git-config-path": "^1.0.1", - "ini": "^1.3.5" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-github-url": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-github-url/-/parse-github-url-1.0.2.tgz", - "integrity": "sha512-kgBf6avCbO3Cn6+RnzRGLkUsv4ZVqv/VfAYkRsyBcgkshNvVBkRn1FEZcW0Jb+npXQWm2vHPnnOqFteZxRRGNw==", - "dev": true, - "bin": { - "parse-github-url": "cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/parse-json": { "version": "5.2.0", "license": "MIT", @@ -34528,24 +34016,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parse-link-header": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/parse-link-header/-/parse-link-header-2.0.0.tgz", - "integrity": "sha512-xjU87V0VyHZybn2RrCX5TIFGxTVZE6zqqZWMPlIKiSKuWh/X5WZdt+w1Ki1nXB+8L/KtL+nZ4iq+sfI6MrhhMw==", - "dev": true, - "dependencies": { - "xtend": "~4.0.1" - } - }, - "node_modules/parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/parse5": { "version": "6.0.1", "dev": true, @@ -34924,12 +34394,6 @@ "node": ">=0.10.0" } }, - "node_modules/pinpoint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pinpoint/-/pinpoint-1.1.0.tgz", - "integrity": "sha512-+04FTD9x7Cls2rihLlo57QDCcHoLBGn5Dk51SwtFBWkUWLxZaBXyNVpCw1S+atvE7GmnFjeaRZ0WLq3UYuqAdg==", - "dev": true - }, "node_modules/pirates": { "version": "4.0.5", "license": "MIT", @@ -35312,28 +34776,6 @@ "node": ">= 0.8" } }, - "node_modules/prettyjson": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/prettyjson/-/prettyjson-1.2.5.tgz", - "integrity": "sha512-rksPWtoZb2ZpT5OVgtmy0KHVM+Dca3iVwWY9ifwhcexfjebtgjg3wmrUt9PvJ59XIYBcknQeYHD8IAnVlh9lAw==", - "dev": true, - "dependencies": { - "colors": "1.4.0", - "minimist": "^1.2.0" - }, - "bin": { - "prettyjson": "bin/prettyjson" - } - }, - "node_modules/prettyjson/node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/process": { "version": "0.11.10", "license": "MIT", @@ -37420,15 +36862,6 @@ "resolved": "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz", "integrity": "sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg==" }, - "node_modules/readline-sync": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz", - "integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/reassure": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/reassure/-/reassure-0.9.0.tgz", @@ -39952,28 +39385,6 @@ "node": ">=4" } }, - "node_modules/supports-hyperlinks": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz", - "integrity": "sha512-HHi5kVSefKaJkGYXbDuKbUGRVxqnWGn3J2e39CYcNJEfWciGq2zYtOhXLTlvrOZW1QU7VX67w7fMmWafHX9Pfw==", - "dev": true, - "dependencies": { - "has-flag": "^2.0.0", - "supports-color": "^5.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-hyperlinks/node_modules/has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha512-P+1n3MnwjR/Epg9BBo1KT8qbye2g2Ou4sFumihwt6I4tsUX7jnLcX4BTOSKg/B1ZrIYMN9FcEnG4x5a7NB8Eng==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "license": "MIT", @@ -42776,12 +42187,6 @@ "default-browser-id": "^1.0.4" } }, - "node_modules/xcase": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/xcase/-/xcase-2.0.1.tgz", - "integrity": "sha512-UmFXIPU+9Eg3E9m/728Bii0lAIuoc+6nbrNUKaRPJOFp91ih44qqGlWtxMB6kXFrRD6po+86ksHM5XHCfk6iPw==", - "dev": true - }, "node_modules/xcode": { "version": "3.0.1", "license": "Apache-2.0", @@ -44731,56 +44136,6 @@ "version": "1.1.3", "dev": true }, - "@gitbeaker/core": { - "version": "21.7.0", - "resolved": "https://registry.npmjs.org/@gitbeaker/core/-/core-21.7.0.tgz", - "integrity": "sha512-cw72rE7tA27wc6JJe1WqeAj9v/6w0S7XJcEji+bRNjTlUfE1zgfW0Gf1mbGUi7F37SOABGCosQLfg9Qe63aIqA==", - "dev": true, - "requires": { - "@gitbeaker/requester-utils": "^21.7.0", - "form-data": "^3.0.0", - "li": "^1.3.0", - "xcase": "^2.0.1" - } - }, - "@gitbeaker/node": { - "version": "21.7.0", - "resolved": "https://registry.npmjs.org/@gitbeaker/node/-/node-21.7.0.tgz", - "integrity": "sha512-OdM3VcTKYYqboOsnbiPcO0XimXXpYK4gTjARBZ6BWc+1LQXKmqo+OH6oUbyxOoaFu9hHECafIt3WZU3NM4sZTg==", - "dev": true, - "requires": { - "@gitbeaker/core": "^21.7.0", - "@gitbeaker/requester-utils": "^21.7.0", - "form-data": "^3.0.0", - "got": "^11.1.4", - "xcase": "^2.0.1" - } - }, - "@gitbeaker/requester-utils": { - "version": "21.7.0", - "resolved": "https://registry.npmjs.org/@gitbeaker/requester-utils/-/requester-utils-21.7.0.tgz", - "integrity": "sha512-eLTaVXlBnh8Qimj6QuMMA06mu/mLcJm3dy8nqhhn/Vm/D25sPrvpGwmbfFyvzj6QujPqtHvFfsCHtyZddL01qA==", - "dev": true, - "requires": { - "form-data": "^3.0.0", - "query-string": "^6.12.1", - "xcase": "^2.0.1" - }, - "dependencies": { - "query-string": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", - "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", - "dev": true, - "requires": { - "decode-uri-component": "^0.2.0", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - } - } - } - }, "@gorhom/portal": { "version": "1.0.14", "requires": { @@ -46138,13 +45493,6 @@ "@octokit/types": "^6.41.0" } }, - "@octokit/plugin-request-log": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", - "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", - "dev": true, - "requires": {} - }, "@octokit/plugin-rest-endpoint-methods": { "version": "5.16.2", "dev": true, @@ -46208,100 +45556,6 @@ } } }, - "@octokit/rest": { - "version": "18.12.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz", - "integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==", - "dev": true, - "requires": { - "@octokit/core": "^3.5.1", - "@octokit/plugin-paginate-rest": "^2.16.8", - "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^5.12.0" - }, - "dependencies": { - "@octokit/auth-token": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", - "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", - "dev": true, - "requires": { - "@octokit/types": "^6.0.3" - } - }, - "@octokit/core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", - "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", - "dev": true, - "requires": { - "@octokit/auth-token": "^2.4.4", - "@octokit/graphql": "^4.5.8", - "@octokit/request": "^5.6.3", - "@octokit/request-error": "^2.0.5", - "@octokit/types": "^6.0.3", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/endpoint": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", - "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", - "dev": true, - "requires": { - "@octokit/types": "^6.0.3", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/graphql": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", - "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", - "dev": true, - "requires": { - "@octokit/request": "^5.6.0", - "@octokit/types": "^6.0.3", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/plugin-paginate-rest": { - "version": "2.21.3", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz", - "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", - "dev": true, - "requires": { - "@octokit/types": "^6.40.0" - } - }, - "@octokit/request": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", - "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", - "dev": true, - "requires": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.1.0", - "@octokit/types": "^6.16.1", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", - "dev": true, - "requires": { - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - } - } - }, "@octokit/types": { "version": "6.41.0", "dev": true, @@ -53754,9 +53008,9 @@ } }, "@ua/react-native-airship": { - "version": "15.2.3", - "resolved": "https://registry.npmjs.org/@ua/react-native-airship/-/react-native-airship-15.2.3.tgz", - "integrity": "sha512-94fgHJcxc4qUy9OmBqt6VuuWggoeCMNIosz3/qwiUNsUVoE1deWcOEG9vULmXcTKX+O9Wal/mKsuPGiFCALTDQ==", + "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==", "requires": {} }, "@vercel/ncc": { @@ -54548,23 +53802,6 @@ "async-limiter": { "version": "1.0.1" }, - "async-retry": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.2.3.tgz", - "integrity": "sha512-tfDb02Th6CE6pJUF2gjW5ZVjsgwlucVXOEQMvEX9JgSJMs9gAX+Nz3xRuJBKuUYjTSYORqvDBORdAQ3LU59g7Q==", - "dev": true, - "requires": { - "retry": "0.12.0" - }, - "dependencies": { - "retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "dev": true - } - } - }, "asynckit": { "version": "0.4.0", "dev": true @@ -55657,12 +54894,6 @@ "version": "1.0.0", "dev": true }, - "buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "dev": true - }, "buffer-fill": { "version": "1.0.0", "dev": true @@ -57121,74 +56352,6 @@ "version": "1.0.8", "dev": true }, - "danger": { - "version": "11.2.6", - "resolved": "https://registry.npmjs.org/danger/-/danger-11.2.6.tgz", - "integrity": "sha512-EEeuDmUcxPGJ166q7Zzz1WEiV+e0qbPopaX4sXxds8U5doGMdw/8oOUOVye7JiHIBuss3KvQWt4YHZeD3jSCfw==", - "dev": true, - "requires": { - "@gitbeaker/node": "^21.3.0", - "@octokit/rest": "^18.12.0", - "async-retry": "1.2.3", - "chalk": "^2.3.0", - "commander": "^2.18.0", - "core-js": "^3.8.2", - "debug": "^4.1.1", - "fast-json-patch": "^3.0.0-1", - "get-stdin": "^6.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.1", - "hyperlinker": "^1.0.0", - "json5": "^2.1.0", - "jsonpointer": "^5.0.0", - "jsonwebtoken": "^9.0.0", - "lodash.find": "^4.6.0", - "lodash.includes": "^4.3.0", - "lodash.isobject": "^3.0.2", - "lodash.keys": "^4.0.8", - "lodash.mapvalues": "^4.6.0", - "lodash.memoize": "^4.1.2", - "memfs-or-file-map-to-github-branch": "^1.2.1", - "micromatch": "^4.0.4", - "node-cleanup": "^2.1.2", - "node-fetch": "^2.6.7", - "override-require": "^1.1.1", - "p-limit": "^2.1.0", - "parse-diff": "^0.7.0", - "parse-git-config": "^2.0.3", - "parse-github-url": "^1.0.2", - "parse-link-header": "^2.0.0", - "pinpoint": "^1.1.0", - "prettyjson": "^1.2.1", - "readline-sync": "^1.4.9", - "regenerator-runtime": "^0.13.9", - "require-from-string": "^2.0.2", - "supports-hyperlinks": "^1.0.1" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "fast-json-patch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz", - "integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==", - "dev": true - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - } - } - }, "data-urls": { "version": "3.0.2", "dev": true, @@ -57663,15 +56826,6 @@ "stream-shift": "^1.0.0" } }, - "ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, "ee-first": { "version": "1.1.1" }, @@ -59087,15 +58241,6 @@ } } }, - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", - "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1" - } - }, "expect": { "version": "29.4.1", "requires": { @@ -59171,9 +58316,9 @@ } }, "expensify-common": { - "version": "git+ssh://git@github.com/Expensify/expensify-common.git#68abe48ad71a98604fdbf5e8e960023ed5807ec2", - "integrity": "sha512-OhM2d7W++k0RVscwBpl0cvKXp3aZmflVawQ9Ebh2RBTdy98VZUwq2iZyCDISGNyETthqqsRq8vxy2lFQZZ19RA==", - "from": "expensify-common@git+ssh://git@github.com/Expensify/expensify-common.git#68abe48ad71a98604fdbf5e8e960023ed5807ec2", + "version": "git+ssh://git@github.com/Expensify/expensify-common.git#c898563fe851d9a4d594fa9afbdd1ddab5971636", + "integrity": "sha512-WjxHYpqebNsPKJC+SBhgsYNSib+8LptZv/BKt8hc67psJjO9JdrTpAHuoZ0n1lCTQ2DhpDERjqTsQbpUqWbgIg==", + "from": "expensify-common@git+ssh://git@github.com/Expensify/expensify-common.git#c898563fe851d9a4d594fa9afbdd1ddab5971636", "requires": { "classnames": "2.3.1", "clipboard": "2.0.4", @@ -59862,12 +59007,6 @@ "readable-stream": "^2.0.0" } }, - "fs-exists-sync": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", - "integrity": "sha512-cR/vflFyPZtrN6b38ZyWxpWdhlXrzZEBawlpBQMq7033xVY7/kg0GDMBK5jg8lDYQckdJ5x/YC88lM3C7VMsLg==", - "dev": true - }, "fs-extra": { "version": "9.1.0", "requires": { @@ -59978,34 +59117,6 @@ "getenv": { "version": "1.0.0" }, - "git-config-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/git-config-path/-/git-config-path-1.0.1.tgz", - "integrity": "sha512-KcJ2dlrrP5DbBnYIZ2nlikALfRhKzNSX0stvv3ImJ+fvC4hXKoV+U+74SV0upg+jlQZbrtQzc0bu6/Zh+7aQbg==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "fs-exists-sync": "^0.1.0", - "homedir-polyfill": "^1.0.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true - } - } - }, "github-slugger": { "version": "1.4.0", "dev": true @@ -60379,15 +59490,6 @@ "react-is": "^16.7.0" } }, - "homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dev": true, - "requires": { - "parse-passwd": "^1.0.0" - } - }, "hosted-git-info": { "version": "4.1.0", "dev": true, @@ -60768,12 +59870,6 @@ } } }, - "hyperlinker": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hyperlinker/-/hyperlinker-1.0.0.tgz", - "integrity": "sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ==", - "dev": true - }, "hyphenate-style-name": { "version": "1.0.4" }, @@ -60895,12 +59991,6 @@ "inherits": { "version": "2.0.4" }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, "inline-style-parser": { "version": "0.1.1", "dev": true @@ -63530,24 +62620,6 @@ "jsonify": { "version": "0.0.0" }, - "jsonpointer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", - "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", - "dev": true - }, - "jsonwebtoken": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", - "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==", - "dev": true, - "requires": { - "jws": "^3.2.2", - "lodash": "^4.17.21", - "ms": "^2.1.1", - "semver": "^7.3.8" - } - }, "jsx-ast-utils": { "version": "3.3.3", "dev": true, @@ -63560,27 +62632,6 @@ "version": "3.1.0", "dev": true }, - "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "dev": true, - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "dev": true, - "requires": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, "keyv": { "version": "4.5.2", "dev": true, @@ -63664,12 +62715,6 @@ "type-check": "~0.4.0" } }, - "li": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/li/-/li-1.3.0.tgz", - "integrity": "sha512-z34TU6GlMram52Tss5mt1m//ifRIpKH5Dqm7yUVOdHI+BQCs9qGPHFaCUTIzsWX7edN30aa2WrPwR7IO10FHaw==", - "dev": true - }, "lie": { "version": "3.1.1", "requires": { @@ -63746,45 +62791,9 @@ "lodash.debounce": { "version": "4.0.8" }, - "lodash.find": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz", - "integrity": "sha512-yaRZoAV3Xq28F1iafWN1+a0rflOej93l1DQUejs3SZ41h2O9UJBoS9aueGjPDgAl4B6tPC0NuuchLKaDQQ3Isg==", - "dev": true - }, - "lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", - "dev": true - }, "lodash.isequal": { "version": "4.5.0" }, - "lodash.isobject": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", - "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==", - "dev": true - }, - "lodash.keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-4.2.0.tgz", - "integrity": "sha512-J79MkJcp7Df5mizHiVNpjoHXLi4HLjh9VLS/M7lQSGoQ+0oQ+lWEigREkqKyizPB1IawvQLLKY8mzEcm1tkyxQ==", - "dev": true - }, - "lodash.mapvalues": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", - "integrity": "sha512-JPFqXFeZQ7BfS00H58kClY7SPVeHertPE0lNuCyZ26/XlN8TvakYD7b9bGyNmXbT/D3BbtPAAmq90gPWqLkxlQ==", - "dev": true - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, "lodash.merge": { "version": "4.6.2", "dev": true @@ -64222,15 +63231,6 @@ "fs-monkey": "^1.0.3" } }, - "memfs-or-file-map-to-github-branch": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/memfs-or-file-map-to-github-branch/-/memfs-or-file-map-to-github-branch-1.2.1.tgz", - "integrity": "sha512-I/hQzJ2a/pCGR8fkSQ9l5Yx+FQ4e7X6blNHyWBm2ojeFLT3GVzGkTj7xnyWpdclrr7Nq4dmx3xrvu70m3ypzAQ==", - "dev": true, - "requires": { - "@octokit/rest": "^16.43.0 || ^17.11.0 || ^18.12.0" - } - }, "memoizerific": { "version": "1.11.3", "dev": true, @@ -65421,12 +64421,6 @@ "dev": true, "optional": true }, - "node-cleanup": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", - "integrity": "sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==", - "dev": true - }, "node-dir": { "version": "0.1.17", "requires": { @@ -65920,12 +64914,6 @@ "os-tmpdir": { "version": "1.0.2" }, - "override-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/override-require/-/override-require-1.1.1.tgz", - "integrity": "sha512-eoJ9YWxFcXbrn2U8FKT6RV+/Kj7fiGAB1VvHzbYKt8xM5ZuKZgCGvnHzDxmreEjcBH28ejg5MiOH4iyY1mQnkg==", - "dev": true - }, "p-all": { "version": "2.1.0", "dev": true, @@ -66044,12 +65032,6 @@ "safe-buffer": "^5.1.1" } }, - "parse-diff": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/parse-diff/-/parse-diff-0.7.1.tgz", - "integrity": "sha512-1j3l8IKcy4yRK2W4o9EYvJLSzpAVwz4DXqCewYyx2vEwk2gcf3DBPqc8Fj4XV3K33OYJ08A8fWwyu/ykD/HUSg==", - "dev": true - }, "parse-entities": { "version": "2.0.0", "dev": true, @@ -66062,23 +65044,6 @@ "is-hexadecimal": "^1.0.0" } }, - "parse-git-config": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/parse-git-config/-/parse-git-config-2.0.3.tgz", - "integrity": "sha512-Js7ueMZOVSZ3tP8C7E3KZiHv6QQl7lnJ+OkbxoaFazzSa2KyEHqApfGbU3XboUgUnq4ZuUmskUpYKTNx01fm5A==", - "dev": true, - "requires": { - "expand-tilde": "^2.0.2", - "git-config-path": "^1.0.1", - "ini": "^1.3.5" - } - }, - "parse-github-url": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-github-url/-/parse-github-url-1.0.2.tgz", - "integrity": "sha512-kgBf6avCbO3Cn6+RnzRGLkUsv4ZVqv/VfAYkRsyBcgkshNvVBkRn1FEZcW0Jb+npXQWm2vHPnnOqFteZxRRGNw==", - "dev": true - }, "parse-json": { "version": "5.2.0", "requires": { @@ -66088,21 +65053,6 @@ "lines-and-columns": "^1.1.6" } }, - "parse-link-header": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/parse-link-header/-/parse-link-header-2.0.0.tgz", - "integrity": "sha512-xjU87V0VyHZybn2RrCX5TIFGxTVZE6zqqZWMPlIKiSKuWh/X5WZdt+w1Ki1nXB+8L/KtL+nZ4iq+sfI6MrhhMw==", - "dev": true, - "requires": { - "xtend": "~4.0.1" - } - }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", - "dev": true - }, "parse5": { "version": "6.0.1", "dev": true @@ -66350,12 +65300,6 @@ "pinkie": "^2.0.0" } }, - "pinpoint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pinpoint/-/pinpoint-1.1.0.tgz", - "integrity": "sha512-+04FTD9x7Cls2rihLlo57QDCcHoLBGn5Dk51SwtFBWkUWLxZaBXyNVpCw1S+atvE7GmnFjeaRZ0WLq3UYuqAdg==", - "dev": true - }, "pirates": { "version": "4.0.5" }, @@ -66604,24 +65548,6 @@ "version": "1.0.3", "dev": true }, - "prettyjson": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/prettyjson/-/prettyjson-1.2.5.tgz", - "integrity": "sha512-rksPWtoZb2ZpT5OVgtmy0KHVM+Dca3iVwWY9ifwhcexfjebtgjg3wmrUt9PvJ59XIYBcknQeYHD8IAnVlh9lAw==", - "dev": true, - "requires": { - "colors": "1.4.0", - "minimist": "^1.2.0" - }, - "dependencies": { - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true - } - } - }, "process": { "version": "0.11.10" }, @@ -68010,12 +66936,6 @@ "resolved": "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz", "integrity": "sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg==" }, - "readline-sync": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz", - "integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==", - "dev": true - }, "reassure": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/reassure/-/reassure-0.9.0.tgz", @@ -69741,24 +68661,6 @@ "has-flag": "^3.0.0" } }, - "supports-hyperlinks": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz", - "integrity": "sha512-HHi5kVSefKaJkGYXbDuKbUGRVxqnWGn3J2e39CYcNJEfWciGq2zYtOhXLTlvrOZW1QU7VX67w7fMmWafHX9Pfw==", - "dev": true, - "requires": { - "has-flag": "^2.0.0", - "supports-color": "^5.0.0" - }, - "dependencies": { - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha512-P+1n3MnwjR/Epg9BBo1KT8qbye2g2Ou4sFumihwt6I4tsUX7jnLcX4BTOSKg/B1ZrIYMN9FcEnG4x5a7NB8Eng==", - "dev": true - } - } - }, "supports-preserve-symlinks-flag": { "version": "1.0.0" }, @@ -71554,12 +70456,6 @@ "default-browser-id": "^1.0.4" } }, - "xcase": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/xcase/-/xcase-2.0.1.tgz", - "integrity": "sha512-UmFXIPU+9Eg3E9m/728Bii0lAIuoc+6nbrNUKaRPJOFp91ih44qqGlWtxMB6kXFrRD6po+86ksHM5XHCfk6iPw==", - "dev": true - }, "xcode": { "version": "3.0.1", "requires": { diff --git a/package.json b/package.json index 9ea0d97d16c3..554fbf28f4a3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.3.21-2", + "version": "1.3.23-3", "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.", @@ -70,13 +70,13 @@ "@react-navigation/native": "6.0.13", "@react-navigation/stack": "6.3.1", "@react-ng/bounds-observer": "^0.2.1", - "@ua/react-native-airship": "^15.2.3", + "@ua/react-native-airship": "^15.2.6", "awesome-phonenumber": "^5.4.0", "babel-plugin-transform-remove-console": "^6.9.4", "babel-polyfill": "^6.26.0", "dom-serializer": "^0.2.2", "domhandler": "^4.3.0", - "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#68abe48ad71a98604fdbf5e8e960023ed5807ec2", + "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#c898563fe851d9a4d594fa9afbdd1ddab5971636", "fbjs": "^3.0.2", "html-entities": "^1.3.1", "htmlparser2": "^7.2.0", @@ -177,7 +177,6 @@ "concurrently": "^5.3.0", "copy-webpack-plugin": "^6.4.1", "css-loader": "^6.7.2", - "danger": "^11.2.6", "diff-so-fancy": "^1.3.0", "dotenv": "^16.0.3", "electron": "22.3.7", diff --git a/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly.js b/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly.js index 5db89d96516f..3886b8fab88e 100644 --- a/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly.js +++ b/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly.js @@ -44,13 +44,14 @@ const BaseAnchorForCommentsOnly = (props) => { linkProps.href = props.href; } const defaultTextStyle = DeviceCapabilities.canUseTouchScreen() || props.isSmallScreenWidth ? {} : styles.userSelectText; + const isEmail = Str.isValidEmailMarkdown(props.href.replace(/mailto:/i, '')); return ( { ReportActionContextMenu.showContextMenu( - Str.isValidEmailMarkdown(props.href.replace(/mailto:/i, '')) ? ContextMenuActions.CONTEXT_MENU_TYPES.EMAIL : ContextMenuActions.CONTEXT_MENU_TYPES.LINK, + isEmail ? ContextMenuActions.CONTEXT_MENU_TYPES.EMAIL : ContextMenuActions.CONTEXT_MENU_TYPES.LINK, event, props.href, lodashGet(linkRef, 'current'), @@ -67,7 +68,7 @@ const BaseAnchorForCommentsOnly = (props) => { accessibilityRole="link" hrefAttrs={{ rel: props.rel, - target: props.target, + target: isEmail ? '_self' : props.target, }} href={linkProps.href} // Add testID so it gets selected as an anchor tag by SelectionScraper diff --git a/src/components/CalendarPicker/index.js b/src/components/CalendarPicker/index.js index cd3679f1b8c6..5b184e5b26d8 100644 --- a/src/components/CalendarPicker/index.js +++ b/src/components/CalendarPicker/index.js @@ -1,6 +1,6 @@ import _ from 'underscore'; import React from 'react'; -import {View, TouchableOpacity, Pressable} from 'react-native'; +import {View} from 'react-native'; import moment from 'moment'; import Str from 'expensify-common/lib/str'; import Text from '../Text'; @@ -14,6 +14,8 @@ import ROUTES from '../../ROUTES'; import CONST from '../../CONST'; import getButtonState from '../../libs/getButtonState'; import * as StyleUtils from '../../styles/StyleUtils'; +import PressableWithFeedback from '../Pressable/PressableWithFeedback'; +import PressableWithoutFeedback from '../Pressable/PressableWithoutFeedback'; class CalendarPicker extends React.PureComponent { constructor(props) { @@ -206,9 +208,12 @@ class CalendarPicker extends React.PureComponent { return ( - - + {monthNames[currentMonthView]} - - - + - + @@ -269,25 +278,27 @@ class CalendarPicker extends React.PureComponent { const isSelected = moment(this.props.value).isSame(moment([currentYearView, currentMonthView, day]), 'day'); return ( - this.onDayPressed(day)} style={styles.calendarDayRoot} accessibilityLabel={day ? day.toString() : undefined} + focusable={Boolean(day)} + accessible={Boolean(day)} > {({hovered, pressed}) => ( {day} )} - + ); })} diff --git a/src/components/CheckboxWithLabel.js b/src/components/CheckboxWithLabel.js index 298f1252d48a..a11af52f6887 100644 --- a/src/components/CheckboxWithLabel.js +++ b/src/components/CheckboxWithLabel.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useState, useEffect} from 'react'; import PropTypes from 'prop-types'; import {View} from 'react-native'; import _ from 'underscore'; @@ -9,6 +9,11 @@ import FormHelpMessage from './FormHelpMessage'; import variables from '../styles/variables'; import PressableWithFeedback from './Pressable/PressableWithFeedback'; +/** + * Returns an error if the required props are not provided + * @param {Object} props + * @returns {Error|null} + */ const requiredPropsCheck = (props) => { if (!props.label && !props.LabelComponent) { return new Error('One of "label" or "LabelComponent" must be provided'); @@ -73,55 +78,55 @@ const defaultProps = { forwardedRef: () => {}, }; -class CheckboxWithLabel extends React.Component { - constructor(props) { - super(props); - - // We need to pick the first value that is strictly a boolean - // https://github.com/Expensify/App/issues/16885#issuecomment-1520846065 - this.isChecked = _.find([props.value, props.defaultValue, props.isChecked], (value) => _.isBoolean(value)); - - this.LabelComponent = props.LabelComponent; - - this.toggleCheckbox = this.toggleCheckbox.bind(this); - } - - toggleCheckbox() { - this.props.onInputChange(!this.isChecked); - this.isChecked = !this.isChecked; - } - - render() { - return ( - - - - - {this.props.label && {this.props.label}} - {this.LabelComponent && } - - - +const CheckboxWithLabel = (props) => { + // We need to pick the first value that is strictly a boolean + // https://github.com/Expensify/App/issues/16885#issuecomment-1520846065 + const [isChecked, setIsChecked] = useState(_.find([props.value, props.defaultValue, props.isChecked], (value) => _.isBoolean(value))); + + const toggleCheckbox = () => { + const newState = !isChecked; + props.onInputChange(newState); + setIsChecked(newState); + }; + + useEffect(() => { + setIsChecked(props.isChecked); + }, [props.isChecked]); + + const LabelComponent = props.LabelComponent; + + return ( + + + + + {props.label && {props.label}} + {LabelComponent && } + - ); - } -} + + + ); +}; CheckboxWithLabel.propTypes = propTypes; CheckboxWithLabel.defaultProps = defaultProps; +CheckboxWithLabel.displayName = 'CheckboxWithLabel'; export default React.forwardRef((props, ref) => ( ( - + {_.map(props.headerEmojis, (headerEmoji, i) => ( {!this.props.isSmallScreenWidth && ( - + @@ -212,6 +221,8 @@ class HeaderWithCloseButton extends Component { )} + {this.props.shouldShowPinButton && } + {this.props.shouldShowThreeDotsButton && ( { const optionItem = SidebarUtils.getOptionData(props.reportID); + + React.useEffect(() => { + ReportActionContextMenu.hideContextMenu(false); + }, [optionItem.isPinned]); + if (!optionItem) { return null; } - let touchableRef = null; + let popoverAnchor = null; const textStyle = props.isFocused ? styles.sidebarLinkActiveText : styles.sidebarLinkText; const textUnreadStyle = optionItem.isUnread ? [textStyle, styles.sidebarLinkTextBold] : [textStyle]; const displayNameStyle = StyleUtils.combineStyles([styles.optionDisplayName, styles.optionDisplayNameCompact, styles.pre, ...textUnreadStyle], props.style); @@ -83,6 +91,28 @@ const OptionRowLHN = (props) => { // If the item is a thread within a workspace, we will show the subtitle as the second line instead of in a pill const alternativeText = optionItem.isThread && optionItem.subtitle ? optionItem.subtitle : optionItem.alternateText; + /** + * Show the ReportActionContextMenu modal popover. + * + * @param {Object} [event] - A press event. + */ + const showPopover = (event) => { + ReportActionContextMenu.showContextMenu( + ContextMenuActions.CONTEXT_MENU_TYPES.REPORT, + event, + '', + popoverAnchor, + props.reportID, + {}, + '', + () => {}, + () => {}, + false, + false, + optionItem.isPinned, + ); + }; + return ( { > {(hovered) => ( - (touchableRef = el)} + (popoverAnchor = el)} onPress={(e) => { if (e) { e.preventDefault(); } - props.onSelectRow(optionItem, touchableRef); + props.onSelectRow(optionItem, popoverAnchor); }} + onSecondaryInteraction={(e) => showPopover(e)} + withoutFocusOnSecondaryInteraction activeOpacity={0.8} style={[ styles.flexRow, @@ -213,7 +245,7 @@ const OptionRowLHN = (props) => { )} - + )} diff --git a/src/components/MagicCodeInput.js b/src/components/MagicCodeInput.js index ce5f7e3a58e8..32c7ec21a920 100644 --- a/src/components/MagicCodeInput.js +++ b/src/components/MagicCodeInput.js @@ -101,6 +101,11 @@ function MagicCodeInput(props) { const [focusedIndex, setFocusedIndex] = useState(0); const [editIndex, setEditIndex] = useState(0); + const blurMagicCodeInput = () => { + inputRefs.current[editIndex].blur(); + setFocusedIndex(undefined); + }; + useImperativeHandle(props.innerRef, () => ({ focus() { setFocusedIndex(0); @@ -113,6 +118,9 @@ function MagicCodeInput(props) { inputRefs.current[0].focus(); props.onChangeText(''); }, + blur() { + blurMagicCodeInput(); + }, })); const validateAndSubmit = () => { @@ -122,8 +130,7 @@ function MagicCodeInput(props) { } // Blurs the input and removes focus from the last input and, if it should submit // on complete, it will call the onFulfill callback. - inputRefs.current[editIndex].blur(); - setFocusedIndex(undefined); + blurMagicCodeInput(); props.onFulfill(props.value); }; @@ -298,8 +305,8 @@ function MagicCodeInput(props) { style={[ styles.textInputContainer, StyleUtils.getHeightOfMagicCodeInput(), - focusedIndex === index ? styles.borderColorFocus : {}, props.hasError || props.errorText ? styles.borderColorDanger : {}, + focusedIndex === index ? styles.borderColorFocus : {}, ]} > {decomposeString(props.value, props.maxLength)[index] || ''} diff --git a/src/components/MoneyRequestHeader.js b/src/components/MoneyRequestHeader.js index 8bf719e679d7..2db66e50370b 100644 --- a/src/components/MoneyRequestHeader.js +++ b/src/components/MoneyRequestHeader.js @@ -93,6 +93,7 @@ const MoneyRequestHeader = (props) => { { }, ]} threeDotsAnchorPosition={styles.threeDotsPopoverOffsetNoCloseButton(props.windowWidth)} - report={moneyRequestReport} + report={props.report} + parentReport={moneyRequestReport} policies={props.policies} personalDetails={props.personalDetails} shouldShowCloseButton={false} diff --git a/src/components/PinButton.js b/src/components/PinButton.js new file mode 100644 index 000000000000..00d9ae3a6077 --- /dev/null +++ b/src/components/PinButton.js @@ -0,0 +1,41 @@ +import React from 'react'; +import {Pressable} from 'react-native'; +import styles from '../styles/styles'; +import themeColors from '../styles/themes/default'; +import Icon from './Icon'; +import Tooltip from './Tooltip'; +import withLocalize, {withLocalizePropTypes} from './withLocalize'; +import reportPropTypes from '../pages/reportPropTypes'; +import * as Report from '../libs/actions/Report'; +import * as Expensicons from './Icon/Expensicons'; +import * as Session from '../libs/actions/Session'; + +const propTypes = { + /** Report to pin */ + report: reportPropTypes, + ...withLocalizePropTypes, +}; + +const defaultProps = { + report: null, +}; + +const PinButton = (props) => ( + + Report.togglePinnedState(props.report.reportID, props.report.isPinned))} + style={[styles.touchableButtonImage]} + > + + + +); + +PinButton.displayName = 'PinButton'; +PinButton.propTypes = propTypes; +PinButton.defaultProps = defaultProps; + +export default withLocalize(PinButton); diff --git a/src/components/Pressable/PressableWithFeedback.js b/src/components/Pressable/PressableWithFeedback.js index 9e475b7169cf..281492568867 100644 --- a/src/components/Pressable/PressableWithFeedback.js +++ b/src/components/Pressable/PressableWithFeedback.js @@ -12,7 +12,17 @@ const omittedProps = ['style', 'pressStyle', 'hoverStyle', 'focusStyle', 'wrappe const PressableWithFeedbackPropTypes = { ..._.omit(GenericPressablePropTypes.pressablePropTypes, omittedProps), + /** + * Determines what opacity value should be applied to the underlaying view when Pressable is pressed. + * To disable dimming, pass 1 as pressDimmingValue + * @default variables.pressDimValue + */ pressDimmingValue: propTypes.number, + /** + * Determines what opacity value should be applied to the underlaying view when pressable is hovered. + * To disable dimming, pass 1 as hoverDimmingValue + * @default variables.hoverDimValue + */ hoverDimmingValue: propTypes.number, }; diff --git a/src/components/PressableWithSecondaryInteraction/index.js b/src/components/PressableWithSecondaryInteraction/index.js index efb64fbb5397..7f43451cbadd 100644 --- a/src/components/PressableWithSecondaryInteraction/index.js +++ b/src/components/PressableWithSecondaryInteraction/index.js @@ -80,6 +80,7 @@ class PressableWithSecondaryInteraction extends Component { style={StyleUtils.combineStyles(this.props.inline ? styles.dInline : this.props.style)} onPressIn={this.props.onPressIn} onLongPress={this.props.onSecondaryInteraction ? this.executeSecondaryInteraction : undefined} + activeOpacity={this.props.activeOpacity} onPressOut={this.props.onPressOut} onPress={this.props.onPress} ref={(el) => (this.pressableRef = el)} diff --git a/src/components/PressableWithSecondaryInteraction/index.native.js b/src/components/PressableWithSecondaryInteraction/index.native.js index 7199223bf18d..7768430c363d 100644 --- a/src/components/PressableWithSecondaryInteraction/index.native.js +++ b/src/components/PressableWithSecondaryInteraction/index.native.js @@ -28,6 +28,7 @@ const PressableWithSecondaryInteraction = (props) => { }} onPressIn={props.onPressIn} onPressOut={props.onPressOut} + activeOpacity={props.activeOpacity} // eslint-disable-next-line react/jsx-props-no-spreading {..._.omit(props, 'onLongPress')} > diff --git a/src/components/PressableWithSecondaryInteraction/pressableWithSecondaryInteractionPropTypes.js b/src/components/PressableWithSecondaryInteraction/pressableWithSecondaryInteractionPropTypes.js index b036404e018d..de0adbe81297 100644 --- a/src/components/PressableWithSecondaryInteraction/pressableWithSecondaryInteractionPropTypes.js +++ b/src/components/PressableWithSecondaryInteraction/pressableWithSecondaryInteractionPropTypes.js @@ -29,6 +29,7 @@ const propTypes = { * * - No support for delayLongPress. * - No support for pressIn and pressOut events. + * - No support for opacity * * Note: Web uses styling instead of Text due to no support of LongPress. Thus above pointers are not valid for web. */ @@ -37,6 +38,9 @@ const propTypes = { /** Disable focus trap for the element on secondary interaction */ withoutFocusOnSecondaryInteraction: PropTypes.bool, + /** Opacity to reduce to when active */ + activeOpacity: PropTypes.number, + /** Used to apply styles to the Pressable */ style: stylePropTypes, }; @@ -48,6 +52,7 @@ const defaultProps = { preventDefaultContextMenu: true, inline: false, withoutFocusOnSecondaryInteraction: false, + activeOpacity: 1, enableLongPressWithHover: false, }; diff --git a/src/components/Tooltip/index.js b/src/components/Tooltip/index.js index 5403cf3055b1..616f548136a0 100644 --- a/src/components/Tooltip/index.js +++ b/src/components/Tooltip/index.js @@ -51,6 +51,9 @@ class Tooltip extends PureComponent { * @param {Object} bounds - updated bounds */ updateBounds(bounds) { + if (bounds.width === 0) { + this.setState({isRendered: false}); + } this.setState({ wrapperWidth: bounds.width, wrapperHeight: bounds.height, diff --git a/src/components/ValidateCode/ValidateCodeModal.js b/src/components/ValidateCode/ValidateCodeModal.js index c356458dc877..344c3107b8cf 100644 --- a/src/components/ValidateCode/ValidateCodeModal.js +++ b/src/components/ValidateCode/ValidateCodeModal.js @@ -39,7 +39,7 @@ const defaultProps = { }; function ValidateCodeModal(props) { - const signInHere = useCallback(() => Session.signInWithValidateCode(props.accountID, props.code, null, props.preferredLocale), [props.accountID, props.code, props.preferredLocale]); + const signInHere = useCallback(() => Session.signInWithValidateCode(props.accountID, props.code, props.preferredLocale), [props.accountID, props.code, props.preferredLocale]); return ( diff --git a/src/components/withWindowDimensions.js b/src/components/withWindowDimensions.js index 14ca3572c482..674a153f7e10 100644 --- a/src/components/withWindowDimensions.js +++ b/src/components/withWindowDimensions.js @@ -14,6 +14,9 @@ const windowDimensionsPropTypes = { // Height of the window windowHeight: PropTypes.number.isRequired, + // Is the window width extra narrow, like on a Fold mobile device? + isExtraSmallScreenWidth: PropTypes.bool.isRequired, + // Is the window width narrow, like on a mobile device? isSmallScreenWidth: PropTypes.bool.isRequired, @@ -75,6 +78,7 @@ class WindowDimensionsProvider extends React.Component { return ( {(insets) => { + const isExtraSmallScreenWidth = this.state.windowWidth <= variables.extraSmallMobileResponsiveWidthBreakpoint; const isSmallScreenWidth = this.state.windowWidth <= variables.mobileResponsiveWidthBreakpoint; const isMediumScreenWidth = !isSmallScreenWidth && this.state.windowWidth <= variables.tabletResponsiveWidthBreakpoint; const isLargeScreenWidth = !isSmallScreenWidth && !isMediumScreenWidth; @@ -83,6 +87,7 @@ class WindowDimensionsProvider extends React.Component { value={{ windowHeight: this.state.windowHeight + getWindowHeightAdjustment(insets), windowWidth: this.state.windowWidth, + isExtraSmallScreenWidth, isSmallScreenWidth, isMediumScreenWidth, isLargeScreenWidth, diff --git a/src/languages/en.js b/src/languages/en.js index 77638749d904..8bfb80d1d22d 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -26,7 +26,7 @@ export default { zoom: 'Zoom', password: 'Password', magicCode: 'Magic code', - twoFactorCode: 'Two factor code', + twoFactorCode: 'Two-factor code', workspaces: 'Workspaces', profile: 'Profile', payments: 'Payments', @@ -195,7 +195,7 @@ export default { requestNewCode: 'You can also', requestNewCodeLink: 'request a new code here', successfulNewCodeRequest: 'Code requested. Please check your device.', - tfaRequiredTitle: 'Two factor authentication\nrequired', + tfaRequiredTitle: 'Two-factor authentication\nrequired', tfaRequiredDescription: 'Please enter the two-factor authentication code\nwhere you are trying to sign in.', }, moneyRequestConfirmationList: { @@ -534,7 +534,7 @@ export default { stepCodes: 'Recovery codes', keepCodesSafe: 'Keep these recovery codes safe!', codesLoseAccess: - 'If you lose access to your authenticator app and don’t have these codes, you will lose access to your account. \n\nNote: Setting up two factor authentication will log you out of all other active sessions.', + 'If you lose access to your authenticator app and don’t have these codes, you will lose access to your account. \n\nNote: Setting up two-factor authentication will log you out of all other active sessions.', stepVerify: 'Verify', scanCode: 'Scan the QR code using your', authenticatorApp: 'authenticator app', @@ -549,8 +549,8 @@ export default { }, twoFactorAuthForm: { error: { - pleaseFillTwoFactorAuth: 'Please enter your two factor code', - incorrect2fa: 'Incorrect two factor authentication code. Please try again.', + pleaseFillTwoFactorAuth: 'Please enter your two-factor code', + incorrect2fa: 'Incorrect two-factor authentication code. Please try again.', }, }, passwordConfirmationScreen: { @@ -693,20 +693,20 @@ export default { error: { pleaseFillMagicCode: 'Please enter your magic code', incorrectMagicCode: 'Incorrect magic code.', - pleaseFillTwoFactorAuth: 'Please enter your two factor code', + pleaseFillTwoFactorAuth: 'Please enter your two-factor code', }, }, passwordForm: { pleaseFillOutAllFields: 'Please fill out all fields', pleaseFillPassword: 'Please enter your password', - pleaseFillTwoFactorAuth: 'Please enter your two factor code', - enterYourTwoFactorAuthenticationCodeToContinue: 'Enter your two factor authentication code to continue', + pleaseFillTwoFactorAuth: 'Please enter your two-factor code', + enterYourTwoFactorAuthenticationCodeToContinue: 'Enter your two-factor authentication code to continue', forgot: 'Forgot?', requiredWhen2FAEnabled: 'Required when 2FA is enabled', error: { incorrectPassword: 'Incorrect password. Please try again.', incorrectLoginOrPassword: 'Incorrect login or password. Please try again.', - incorrect2fa: 'Incorrect two factor authentication code. Please try again.', + incorrect2fa: 'Incorrect two-factor authentication code. Please try again.', twoFactorAuthenticationEnabled: 'You have 2FA enabled on this account. Please sign in using your email or phone number.', invalidLoginOrPassword: 'Invalid login or password. Please try again or reset your password.', unableToResetPassword: diff --git a/src/libs/Permissions.js b/src/libs/Permissions.js index 84d0a3d0154b..d3e407260e20 100644 --- a/src/libs/Permissions.js +++ b/src/libs/Permissions.js @@ -102,14 +102,6 @@ function canUseTasks(betas) { return _.contains(betas, CONST.BETAS.TASKS) || _.contains(betas, CONST.BETAS.ALL); } -/** - * @param {Array} betas - * @returns {Boolean} - */ -function canUseThreads(betas) { - return _.contains(betas, CONST.BETAS.THREADS) || canUseAllBetas(betas); -} - export default { canUseChronos, canUseIOU, @@ -122,5 +114,4 @@ export default { canUsePolicyExpenseChat, canUsePasswordlessLogins, canUseTasks, - canUseThreads, }; diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js index d5551e06ba4a..6b6a9d5637c6 100644 --- a/src/libs/ReportActionsUtils.js +++ b/src/libs/ReportActionsUtils.js @@ -30,6 +30,14 @@ Onyx.connect({ callback: (val) => (isNetworkOffline = lodashGet(val, 'isOffline', false)), }); +/** + * @param {Object} reportAction + * @returns {Boolean} + */ +function isCreatedAction(reportAction) { + return lodashGet(reportAction, 'actionName') === CONST.REPORT.ACTIONS.TYPE.CREATED; +} + /** * @param {Object} reportAction * @returns {Boolean} @@ -229,6 +237,10 @@ function getLastVisibleMessageText(reportID, actionsToMerge = {}) { return CONST.ATTACHMENT_MESSAGE_TEXT; } + if (isCreatedAction(lastVisibleAction)) { + return ''; + } + const messageText = lodashGet(message, 'text', ''); return String(messageText).replace(CONST.REGEX.AFTER_FIRST_LINE_BREAK, '').substring(0, CONST.REPORT.LAST_MESSAGE_TEXT_MAX_LENGTH).trim(); } diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index aeb170ddbd44..8231886e8632 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -637,10 +637,11 @@ function hasExpensifyEmails(emails) { * Whether the time row should be shown for a report. * @param {Array} personalDetails * @param {Object} report + * @param {String} login * @return {Boolean} */ -function canShowReportRecipientLocalTime(personalDetails, report) { - const reportParticipants = _.without(lodashGet(report, 'participants', []), sessionEmail); +function canShowReportRecipientLocalTime(personalDetails, report, login) { + const reportParticipants = _.without(lodashGet(report, 'participants', []), login); const participantsWithoutExpensifyEmails = _.difference(reportParticipants, CONST.EXPENSIFY_EMAILS); const hasMultipleParticipants = participantsWithoutExpensifyEmails.length > 1; const reportRecipient = personalDetails[participantsWithoutExpensifyEmails[0]]; diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 79a5de9ee7b4..7910f0e5e798 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -686,16 +686,17 @@ function markCommentAsUnread(reportID, reportActionCreated) { /** * Toggles the pinned state of the report. * - * @param {Object} report + * @param {Object} reportID + * @param {Boolean} isPinnedChat */ -function togglePinnedState(report) { - const pinnedValue = !report.isPinned; +function togglePinnedState(reportID, isPinnedChat) { + const pinnedValue = !isPinnedChat; // Optimistically pin/unpin the report before we send out the command const optimisticData = [ { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: {isPinned: pinnedValue}, }, ]; @@ -703,7 +704,7 @@ function togglePinnedState(report) { API.write( 'TogglePinnedChat', { - reportID: report.reportID, + reportID, pinnedValue, }, {optimisticData}, diff --git a/src/libs/actions/Session/index.js b/src/libs/actions/Session/index.js index 4bcccaa08ddc..9cb3c127b287 100644 --- a/src/libs/actions/Session/index.js +++ b/src/libs/actions/Session/index.js @@ -419,7 +419,7 @@ function signIn(password, validateCode, twoFactorAuthCode, preferredLocale = CON }); } -function signInWithValidateCode(accountID, code, twoFactorAuthCode, preferredLocale = CONST.LOCALES.DEFAULT) { +function signInWithValidateCode(accountID, code, preferredLocale = CONST.LOCALES.DEFAULT, twoFactorAuthCode = '') { // If this is called from the 2fa step, get the validateCode directly from onyx // instead of the one passed from the component state because the state is changing when this method is called. const validateCode = twoFactorAuthCode ? credentials.validateCode : code; @@ -495,8 +495,8 @@ function signInWithValidateCode(accountID, code, twoFactorAuthCode, preferredLoc }); } -function signInWithValidateCodeAndNavigate(accountID, validateCode, twoFactorAuthCode, preferredLocale = CONST.LOCALES.DEFAULT) { - signInWithValidateCode(accountID, validateCode, twoFactorAuthCode, preferredLocale); +function signInWithValidateCodeAndNavigate(accountID, validateCode, preferredLocale = CONST.LOCALES.DEFAULT, twoFactorAuthCode = '') { + signInWithValidateCode(accountID, validateCode, preferredLocale, twoFactorAuthCode); Navigation.navigate(ROUTES.HOME); } diff --git a/src/pages/ReimbursementAccount/ValidationStep.js b/src/pages/ReimbursementAccount/ValidationStep.js index ab8010f1b347..7b2a5b0d6df1 100644 --- a/src/pages/ReimbursementAccount/ValidationStep.js +++ b/src/pages/ReimbursementAccount/ValidationStep.js @@ -41,7 +41,7 @@ const propTypes = { /** User's account who is setting up bank account */ account: PropTypes.shape({ - /** If user has Two factor authentication enabled */ + /** If user has two-factor authentication enabled */ requiresTwoFactorAuth: PropTypes.bool, }), }; diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index 6f1874eb3154..f1483bfb102c 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -1,4 +1,4 @@ -import React, {Component} from 'react'; +import React, {useMemo} from 'react'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; @@ -57,146 +57,145 @@ const defaultProps = { personalDetails: {}, }; -class ReportDetailsPage extends Component { - getPolicy() { - return this.props.policies[`${ONYXKEYS.COLLECTION.POLICY}${this.props.report.policyID}`]; - } +const ReportDetailsPage = (props) => { + const policy = useMemo(() => props.policies[`${ONYXKEYS.COLLECTION.POLICY}${props.report.policyID}`], [props.policies, props.report.policyID]); + const isPolicyAdmin = useMemo(() => PolicyUtils.isPolicyAdmin(policy), [policy]); + const isPolicyExpenseChat = useMemo(() => ReportUtils.isPolicyExpenseChat(props.report), [props.report]); + const isChatRoom = useMemo(() => ReportUtils.isChatRoom(props.report), [props.report]); + const isThread = useMemo(() => ReportUtils.isThread(props.report), [props.report]); + const isUserCreatedPolicyRoom = useMemo(() => ReportUtils.isUserCreatedPolicyRoom(props.report), [props.report]); + const isArchivedRoom = useMemo(() => ReportUtils.isArchivedRoom(props.report), [props.report]); - getMenuItems() { - const menuItems = [ + // eslint-disable-next-line react-hooks/exhaustive-deps -- policy is a dependency because `getChatRoomSubtitle` calls `getPolicyName` which in turn retrieves the value from the `policy` value stored in Onyx + const chatRoomSubtitle = useMemo(() => ReportUtils.getChatRoomSubtitle(props.report), [props.report, policy]); + const canLeaveRoom = useMemo(() => ReportUtils.canLeaveRoom(props.report, !_.isEmpty(policy)), [policy, props.report]); + const participants = useMemo(() => lodashGet(props.report, 'participants', []), [props.report]); + + const menuItems = useMemo(() => { + if (isArchivedRoom) { + return []; + } + + const items = [ { key: CONST.REPORT_DETAILS_MENU_ITEM.SHARE_CODE, translationKey: 'common.shareCode', icon: Expensicons.QrCode, - action: () => Navigation.navigate(ROUTES.getReportShareCodeRoute(this.props.report.reportID)), + action: () => Navigation.navigate(ROUTES.getReportShareCodeRoute(props.report.reportID)), }, ]; - if (ReportUtils.isArchivedRoom(this.props.report)) { - return []; - } - - if (lodashGet(this.props.report, 'participants', []).length) { - menuItems.push({ + if (participants.length) { + items.push({ key: CONST.REPORT_DETAILS_MENU_ITEM.MEMBERS, translationKey: 'common.members', icon: Expensicons.Users, - subtitle: lodashGet(this.props.report, 'participants', []).length, + subtitle: participants.length, action: () => { - Navigation.navigate(ROUTES.getReportParticipantsRoute(this.props.report.reportID)); + Navigation.navigate(ROUTES.getReportParticipantsRoute(props.report.reportID)); }, }); } - if (ReportUtils.isPolicyExpenseChat(this.props.report) || ReportUtils.isChatRoom(this.props.report) || ReportUtils.isThread(this.props.report)) { - menuItems.push({ + if (isPolicyExpenseChat || isChatRoom || isThread) { + items.push({ key: CONST.REPORT_DETAILS_MENU_ITEM.SETTINGS, translationKey: 'common.settings', icon: Expensicons.Gear, action: () => { - Navigation.navigate(ROUTES.getReportSettingsRoute(this.props.report.reportID)); + Navigation.navigate(ROUTES.getReportSettingsRoute(props.report.reportID)); }, }); } - const policy = this.getPolicy(); - const isThread = ReportUtils.isThread(this.props.report); - if (ReportUtils.isUserCreatedPolicyRoom(this.props.report) || ReportUtils.canLeaveRoom(this.props.report, !_.isEmpty(policy)) || isThread) { - menuItems.push({ + if (isUserCreatedPolicyRoom || canLeaveRoom || isThread) { + items.push({ key: CONST.REPORT_DETAILS_MENU_ITEM.LEAVE_ROOM, translationKey: isThread ? 'common.leaveThread' : 'common.leaveRoom', icon: Expensicons.Exit, - action: () => Report.leaveRoom(this.props.report.reportID), + action: () => Report.leaveRoom(props.report.reportID), }); } - return menuItems; - } - - render() { - const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(this.props.report); - const isChatRoom = ReportUtils.isChatRoom(this.props.report); - const isThread = ReportUtils.isThread(this.props.report); - const chatRoomSubtitle = ReportUtils.getChatRoomSubtitle(this.props.report); - const participants = lodashGet(this.props.report, 'participants', []); - const isMultipleParticipant = participants.length > 1; - const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips( - OptionsListUtils.getPersonalDetailsForLogins(participants, this.props.personalDetails), - isMultipleParticipant, - ); - const menuItems = this.getMenuItems(); - const isPolicyAdmin = PolicyUtils.isPolicyAdmin(this.getPolicy()); - const chatRoomSubtitleText = ( - - {chatRoomSubtitle} - - ); - return ( - - - Navigation.goBack()} - onCloseButtonPress={() => Navigation.dismissModal()} - /> - - - - - - - - - - - {isPolicyAdmin ? ( - { - Navigation.navigate(ROUTES.getWorkspaceInitialRoute(this.props.report.policyID)); - }} - > - {chatRoomSubtitleText} - - ) : ( - chatRoomSubtitleText - )} - - + return items; + }, [props.report.reportID, participants, isArchivedRoom, isPolicyExpenseChat, isChatRoom, isThread, isUserCreatedPolicyRoom, canLeaveRoom]); + + const displayNamesWithTooltips = useMemo(() => { + const hasMultipleParticipants = participants.length > 1; + return ReportUtils.getDisplayNamesWithTooltips(OptionsListUtils.getPersonalDetailsForLogins(participants, props.personalDetails), hasMultipleParticipants); + }, [participants, props.personalDetails]); + + const chatRoomSubtitleText = chatRoomSubtitle ? ( + + {chatRoomSubtitle} + + ) : null; + + return ( + + + Navigation.goBack()} + onCloseButtonPress={() => Navigation.dismissModal()} + /> + + + + - {_.map(menuItems, (item) => { - const brickRoadIndicator = - ReportUtils.hasReportNameError(this.props.report) && item.key === CONST.REPORT_DETAILS_MENU_ITEM.SETTINGS ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; - return ( - + + - ); - })} - - - - ); - } -} + + {isPolicyAdmin ? ( + { + Navigation.navigate(ROUTES.getWorkspaceInitialRoute(props.report.policyID)); + }} + > + {chatRoomSubtitleText} + + ) : ( + chatRoomSubtitleText + )} + + + {_.map(menuItems, (item) => { + const brickRoadIndicator = + ReportUtils.hasReportNameError(props.report) && item.key === CONST.REPORT_DETAILS_MENU_ITEM.SETTINGS ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; + return ( + + ); + })} + + + + ); +}; +ReportDetailsPage.displayName = 'ReportDetailsPage'; ReportDetailsPage.propTypes = propTypes; ReportDetailsPage.defaultProps = defaultProps; + export default compose( withLocalize, withReportOrNotFound, diff --git a/src/pages/ValidateLoginPage/index.js b/src/pages/ValidateLoginPage/index.js index 2f6e9c07e6e4..97461b42ab92 100644 --- a/src/pages/ValidateLoginPage/index.js +++ b/src/pages/ValidateLoginPage/index.js @@ -49,7 +49,7 @@ class ValidateLoginPage extends Component { // because we don't want to block the user with the interstitial page. Navigation.goBack(false); } else { - Session.signInWithValidateCodeAndNavigate(accountID, validateCode, null, this.props.preferredLocale); + Session.signInWithValidateCodeAndNavigate(accountID, validateCode, this.props.preferredLocale); } } else { User.validateLogin(accountID, validateCode); diff --git a/src/pages/ValidateLoginPage/index.website.js b/src/pages/ValidateLoginPage/index.website.js index 4f048dc30659..89cdcef836ab 100644 --- a/src/pages/ValidateLoginPage/index.website.js +++ b/src/pages/ValidateLoginPage/index.website.js @@ -83,7 +83,7 @@ class ValidateLoginPage extends Component { } // The user has initiated the sign in process on the same browser, in another tab. - Session.signInWithValidateCode(this.getAccountID(), this.getValidateCode(), null, this.props.preferredLocale); + Session.signInWithValidateCode(this.getAccountID(), this.getValidateCode(), this.props.preferredLocale); } componentDidUpdate() { @@ -117,20 +117,21 @@ class ValidateLoginPage extends Component { } render() { - const isTfaRequired = lodashGet(this.props, 'account.requiresTwoFactorAuth', false); + const is2FARequired = lodashGet(this.props, 'account.requiresTwoFactorAuth', false); const isSignedIn = Boolean(lodashGet(this.props, 'session.authToken', null)); + const currentAuthState = this.getAutoAuthState(); return ( <> - {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.FAILED && } - {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.JUST_SIGNED_IN && (!isTfaRequired || isSignedIn) && } - {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.JUST_SIGNED_IN && isTfaRequired && !isSignedIn && } - {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.NOT_STARTED && ( + {currentAuthState === CONST.AUTO_AUTH_STATE.FAILED && } + {currentAuthState === CONST.AUTO_AUTH_STATE.JUST_SIGNED_IN && is2FARequired && !isSignedIn && } + {currentAuthState === CONST.AUTO_AUTH_STATE.NOT_STARTED && isSignedIn && } + {currentAuthState === CONST.AUTO_AUTH_STATE.NOT_STARTED && !isSignedIn && ( )} - {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.SIGNING_IN && } + {currentAuthState === CONST.AUTO_AUTH_STATE.SIGNING_IN && } ); } diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 2d9d4cd4f265..73cc0fc4a535 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -9,7 +9,6 @@ import themeColors from '../../styles/themes/default'; import Icon from '../../components/Icon'; import * as Expensicons from '../../components/Icon/Expensicons'; import compose from '../../libs/compose'; -import * as Report from '../../libs/actions/Report'; import withWindowDimensions, {windowDimensionsPropTypes} from '../../components/withWindowDimensions'; import MultipleAvatars from '../../components/MultipleAvatars'; import SubscriptAvatar from '../../components/SubscriptAvatar'; @@ -28,7 +27,7 @@ import ONYXKEYS from '../../ONYXKEYS'; import ThreeDotsMenu from '../../components/ThreeDotsMenu'; import * as Task from '../../libs/actions/Task'; import reportActionPropTypes from './report/reportActionPropTypes'; -import * as Session from '../../libs/actions/Session'; +import PinButton from '../../components/PinButton'; const propTypes = { /** Toggles the navigationMenu open and closed */ @@ -196,17 +195,7 @@ const HeaderView = (props) => { guideCalendarLink={guideCalendarLink} /> )} - - Report.togglePinnedState(props.report))} - style={[styles.touchableButtonImage]} - > - - - + {shouldShowThreeDotsButton && ( { const message = _.last(lodashGet(reportAction, 'message', [{}])); const isAttachment = _.has(reportAction, 'isAttachment') ? reportAction.isAttachment : ReportUtils.isReportMessageAttachment(message); - return isAttachment && message.html !== CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML && reportAction.reportActionID; + return ( + isAttachment && + message.html !== CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML && + reportAction.reportActionID && + !lodashGet(reportAction, 'originalMessage.isDeletedParentAction', false) + ); }, onPress: (closePopover, {reportAction}) => { const message = _.last(lodashGet(reportAction, 'message', [{}])); @@ -113,10 +119,7 @@ export default [ successTextTranslateKey: '', successIcon: null, shouldShow: (type, reportAction, isArchivedRoom, betas, anchor, isChronosReport, reportID) => - Permissions.canUseThreads(betas) && - type === CONTEXT_MENU_TYPES.REPORT_ACTION && - reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT && - !ReportUtils.isThreadFirstChat(reportAction, reportID), + type === CONTEXT_MENU_TYPES.REPORT_ACTION && reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT && !ReportUtils.isThreadFirstChat(reportAction, reportID), onPress: (closePopover, {reportAction, reportID}) => { Report.navigateToAndOpenChildReport(lodashGet(reportAction, 'childReportID', '0'), reportAction, reportID); if (closePopover) { @@ -147,7 +150,7 @@ export default [ Clipboard.setString(selection.replace('mailto:', '')); hideContextMenu(true, ReportActionComposeFocusManager.focus); }, - getDescription: () => {}, + getDescription: (selection) => selection.replace('mailto:', ''), }, { textTranslateKey: 'reportActionContextMenu.copyToClipboard', @@ -266,6 +269,30 @@ export default [ }, getDescription: () => {}, }, + { + textTranslateKey: 'common.pin', + icon: Expensicons.Pin, + shouldShow: (type, reportAction, isArchivedRoom, betas, anchor, isChronosReport, reportID, isPinnedChat) => type === CONTEXT_MENU_TYPES.REPORT && !isPinnedChat, + onPress: (closePopover, {reportID}) => { + Report.togglePinnedState(reportID, false); + if (closePopover) { + hideContextMenu(false); + } + }, + getDescription: () => {}, + }, + { + textTranslateKey: 'common.unPin', + icon: Expensicons.Pin, + shouldShow: (type, reportAction, isArchivedRoom, betas, anchor, isChronosReport, reportID, isPinnedChat) => type === CONTEXT_MENU_TYPES.REPORT && isPinnedChat, + onPress: (closePopover, {reportID}) => { + Report.togglePinnedState(reportID, true); + if (closePopover) { + hideContextMenu(false); + } + }, + getDescription: () => {}, + }, { textTranslateKey: 'reportActionContextMenu.flagAsOffensive', icon: Expensicons.Flag, diff --git a/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.js b/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.js index da3558181dd6..770968567139 100644 --- a/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.js +++ b/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.js @@ -39,6 +39,7 @@ class PopoverReportActionContextMenu extends React.Component { }, isArchivedRoom: false, isChronosReport: false, + isPinnedChat: false, }; this.onPopoverShow = () => {}; this.onPopoverHide = () => {}; @@ -126,9 +127,22 @@ class PopoverReportActionContextMenu extends React.Component { * @param {Function} [onHide] - Run a callback when Menu is hidden * @param {Boolean} isArchivedRoom - Whether the provided report is an archived room * @param {Boolean} isChronosReport - Flag to check if the chat participant is Chronos - * @param {String} childReportID - ReportAction childReportID + * @param {Boolean} isPinnedChat - Flag to check if the chat is pinned in the LHN. Used for the Pin/Unpin action */ - showContextMenu(type, event, selection, contextMenuAnchor, reportID, reportAction, draftMessage, onShow = () => {}, onHide = () => {}, isArchivedRoom, isChronosReport) { + showContextMenu( + type, + event, + selection, + contextMenuAnchor, + reportID, + reportAction, + draftMessage, + onShow = () => {}, + onHide = () => {}, + isArchivedRoom = false, + isChronosReport = false, + isPinnedChat = false, + ) { const nativeEvent = event.nativeEvent || {}; this.contextMenuAnchor = contextMenuAnchor; this.contextMenuTargetNode = nativeEvent.target; @@ -158,6 +172,7 @@ class PopoverReportActionContextMenu extends React.Component { reportActionDraftMessage: draftMessage, isArchivedRoom, isChronosReport, + isPinnedChat, }); }); } @@ -246,6 +261,7 @@ class PopoverReportActionContextMenu extends React.Component { shouldSetModalVisibilityForDeleteConfirmation: true, isArchivedRoom: false, isChronosReport: false, + isPinnedChat: false, }); } @@ -292,6 +308,7 @@ class PopoverReportActionContextMenu extends React.Component { selection={this.state.selection} isArchivedRoom={this.state.isArchivedRoom} isChronosReport={this.state.isChronosReport} + isPinnedChat={this.state.isPinnedChat} anchor={this.contextMenuTargetNode} contentRef={this.contentRef} /> diff --git a/src/pages/home/report/ContextMenu/ReportActionContextMenu.js b/src/pages/home/report/ContextMenu/ReportActionContextMenu.js index d3d0ce226794..ea68cae8c710 100644 --- a/src/pages/home/report/ContextMenu/ReportActionContextMenu.js +++ b/src/pages/home/report/ContextMenu/ReportActionContextMenu.js @@ -5,7 +5,7 @@ const contextMenuRef = React.createRef(); /** * Show the ReportActionContextMenu modal popover. * - * @param {string} type - the context menu type to display [EMAIL, LINK, REPORT_ACTION] + * @param {string} type - the context menu type to display [EMAIL, LINK, REPORT_ACTION, REPORT] * @param {Object} [event] - A press event. * @param {String} [selection] - Copied content. * @param {Element} contextMenuAnchor - popoverAnchor @@ -16,7 +16,7 @@ const contextMenuRef = React.createRef(); * @param {Function} [onHide=() => {}] - Run a callback when Menu is hidden * @param {Boolean} isArchivedRoom - Whether the provided report is an archived room * @param {Boolean} isChronosReport - Flag to check if the chat participant is Chronos - * @param {String} childReportID - The child report (thread) of this action + * @param {Boolean} isPinnedChat - Flag to check if the chat is pinned in the LHN. Used for the Pin/Unpin action */ function showContextMenu( type, @@ -30,12 +30,12 @@ function showContextMenu( onHide = () => {}, isArchivedRoom = false, isChronosReport = false, - childReportID = '0', + isPinnedChat = false, ) { if (!contextMenuRef.current) { return; } - contextMenuRef.current.showContextMenu(type, event, selection, contextMenuAnchor, reportID, reportAction, draftMessage, onShow, onHide, isArchivedRoom, isChronosReport, childReportID); + contextMenuRef.current.showContextMenu(type, event, selection, contextMenuAnchor, reportID, reportAction, draftMessage, onShow, onHide, isArchivedRoom, isChronosReport, isPinnedChat); } /** diff --git a/src/pages/home/report/ParticipantLocalTime.js b/src/pages/home/report/ParticipantLocalTime.js index 6b37e02484f0..ceafc5dcfad2 100644 --- a/src/pages/home/report/ParticipantLocalTime.js +++ b/src/pages/home/report/ParticipantLocalTime.js @@ -53,7 +53,7 @@ class ParticipantLocalTime extends PureComponent { } render() { - const reportRecipientDisplayName = this.props.participant.firstName || this.props.participant.displayName; + const reportRecipientDisplayName = lodashGet(this.props, 'participant.firstName') || lodashGet(this.props, 'participant.displayName'); return ( diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js index c789b03ab0b9..6c289eb13123 100644 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -183,7 +183,6 @@ class ReportActionCompose extends React.Component { this.updateNumberOfLines = this.updateNumberOfLines.bind(this); this.showPopoverMenu = this.showPopoverMenu.bind(this); this.comment = props.comment; - this.setShouldBlockEmojiCalcToFalse = this.setShouldBlockEmojiCalcToFalse.bind(this); // React Native will retain focus on an input for native devices but web/mWeb behave differently so we have some focus management // code that will refocus the compose input after a user closes a modal or some other actions, see usage of ReportActionComposeFocusManager @@ -195,6 +194,10 @@ class ReportActionCompose extends React.Component { this.shouldAutoFocus = !props.modal.isVisible && (this.shouldFocusInputOnScreenFocus || this.isEmptyChat()) && props.shouldShowComposeInput; + // These variables are used to decide whether to block the suggestions list from showing to prevent flickering + this.shouldBlockEmojiCalc = false; + this.shouldBlockMentionCalc = false; + // For mobile Safari, updating the selection prop on an unfocused input will cause it to automatically gain focus // and subsequent programmatic focus shifts (e.g., modal focus trap) to show the blue frame (:focus-visible style), // so we need to ensure that it is only updated after focus. @@ -285,6 +288,8 @@ class ReportActionCompose extends React.Component { this.setState({selection: e.nativeEvent.selection}); if (!this.state.value || e.nativeEvent.selection.end < 1) { this.resetSuggestions(); + this.shouldBlockEmojiCalc = false; + this.shouldBlockMentionCalc = false; return; } this.calculateEmojiSuggestion(); @@ -423,13 +428,6 @@ class ReportActionCompose extends React.Component { } } - // eslint-disable-next-line rulesdir/prefer-early-return - setShouldBlockEmojiCalcToFalse() { - if (this.state && this.state.shouldBlockEmojiCalc) { - this.setState({shouldBlockEmojiCalc: false}); - } - } - /** * Determines if we can show the task option * @param {Array} reportParticipants @@ -462,7 +460,7 @@ class ReportActionCompose extends React.Component { getMentionOptions(personalDetails, searchValue = '') { const suggestions = []; - if (CONST.AUTO_COMPLETE_SUGGESTER.HERE_TEXT.includes(searchValue)) { + if (CONST.AUTO_COMPLETE_SUGGESTER.HERE_TEXT.includes(searchValue.toLowerCase())) { suggestions.push({ text: CONST.AUTO_COMPLETE_SUGGESTER.HERE_TEXT, alternateText: this.props.translate('mentionSuggestions.hereAlternateText'), @@ -513,10 +511,11 @@ class ReportActionCompose extends React.Component { * Calculates and cares about the content of an Emoji Suggester */ calculateEmojiSuggestion() { - if (this.state.shouldBlockEmojiCalc) { - this.setState({shouldBlockEmojiCalc: false}); + if (this.shouldBlockEmojiCalc) { + this.shouldBlockEmojiCalc = false; return; } + const leftString = this.state.value.substring(0, this.state.selection.end); const colonIndex = leftString.lastIndexOf(':'); const isCurrentlyShowingEmojiSuggestion = this.isEmojiCode(this.state.value, this.state.selection.end); @@ -543,6 +542,11 @@ class ReportActionCompose extends React.Component { } calculateMentionSuggestion() { + if (this.shouldBlockMentionCalc) { + this.shouldBlockMentionCalc = false; + return; + } + const valueAfterTheCursor = this.state.value.substring(this.state.selection.end); const indexOfFirstWhitespaceCharOrEmojiAfterTheCursor = valueAfterTheCursor.search(CONST.REGEX.SPECIAL_CHAR_OR_EMOJI); @@ -906,8 +910,8 @@ class ReportActionCompose extends React.Component { const reportParticipants = _.without(lodashGet(this.props.report, 'participants', []), this.props.currentUserPersonalDetails.login); const participantsWithoutExpensifyEmails = _.difference(reportParticipants, CONST.EXPENSIFY_EMAILS); const reportRecipient = this.props.personalDetails[participantsWithoutExpensifyEmails[0]]; - - const shouldShowReportRecipientLocalTime = ReportUtils.canShowReportRecipientLocalTime(this.props.personalDetails, this.props.report) && !this.props.isComposerFullSize; + const shouldShowReportRecipientLocalTime = + ReportUtils.canShowReportRecipientLocalTime(this.props.personalDetails, this.props.report, this.props.currentUserPersonalDetails.login) && !this.props.isComposerFullSize; // Prevents focusing and showing the keyboard while the drawer is covering the chat. const isComposeDisabled = this.props.isDrawerOpen && this.props.isSmallScreenWidth; @@ -916,6 +920,7 @@ class ReportActionCompose extends React.Component { const shouldUseFocusedColor = !isBlockedFromConcierge && !this.props.disabled && (this.state.isFocused || this.state.isDraggingOver); const hasExceededMaxCommentLength = this.state.hasExceededMaxCommentLength; const isFullComposerAvailable = this.state.isFullComposerAvailable && !_.isEmpty(this.state.value); + const hasReportRecipient = _.isObject(reportRecipient) && !_.isEmpty(reportRecipient); return ( - {shouldShowReportRecipientLocalTime && } + {shouldShowReportRecipientLocalTime && hasReportRecipient && } this.setState({isAttachmentPreviewActive: true})} onModalHide={() => { - this.setShouldBlockEmojiCalcToFalse(); + this.shouldBlockEmojiCalc = false; + this.shouldBlockMentionCalc = false; this.setState({isAttachmentPreviewActive: false}); }} > @@ -1025,10 +1031,11 @@ class ReportActionCompose extends React.Component { icon: Expensicons.Paperclip, text: this.props.translate('reportActionCompose.addAttachment'), onSelected: () => { - // Set a flag to block emoji calculation until we're finished using the file picker, + // Set a flag to block suggestion calculation until we're finished using the file picker, // which will stop any flickering as the file picker opens on non-native devices. if (this.willBlurTextInputOnTapOutside) { - this.setState({shouldBlockEmojiCalc: true}); + this.shouldBlockEmojiCalc = true; + this.shouldBlockMentionCalc = true; } openPicker({ @@ -1082,7 +1089,10 @@ class ReportActionCompose extends React.Component { this.setIsFocused(false); this.resetSuggestions(); }} - onClick={this.setShouldBlockEmojiCalcToFalse} + onClick={() => { + this.shouldBlockEmojiCalc = false; + this.shouldBlockMentionCalc = false; + }} onPasteFile={displayFileInModal} shouldClear={this.state.textInputShouldClear} onClear={() => this.setTextInputShouldClear(false)} diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index c6eb8f9240f7..6a31d96bd23a 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -52,7 +52,6 @@ import ReportPreview from '../../../components/ReportActionItem/ReportPreview'; import ReportActionItemDraft from './ReportActionItemDraft'; import TaskPreview from '../../../components/ReportActionItem/TaskPreview'; import TaskAction from '../../../components/ReportActionItem/TaskAction'; -import Permissions from '../../../libs/Permissions'; import * as Session from '../../../libs/actions/Session'; import {hideContextMenu} from './ContextMenu/ReportActionContextMenu'; @@ -92,9 +91,6 @@ const propTypes = { /** All of the personalDetails */ personalDetails: PropTypes.objectOf(personalDetailsPropType), - - /** List of betas available to current user */ - betas: PropTypes.arrayOf(PropTypes.string), }; const defaultProps = { @@ -103,7 +99,6 @@ const defaultProps = { personalDetails: {}, shouldShowSubscriptAvatar: false, hasOutstandingIOU: false, - betas: [], }; function ReportActionItem(props) { @@ -172,7 +167,6 @@ function ReportActionItem(props) { toggleContextMenuFromActiveReportAction, ReportUtils.isArchivedRoom(props.report), ReportUtils.chatIncludesChronos(props.report), - props.action.childReportID, ); }, [props.draftMessage, props.action, props.report, toggleContextMenuFromActiveReportAction], @@ -327,8 +321,7 @@ function ReportActionItem(props) { const numberOfThreadReplies = _.get(props, ['action', 'childVisibleActionCount'], 0); const hasReplies = numberOfThreadReplies > 0; - const shouldDisplayThreadReplies = - hasReplies && props.action.childCommenterCount && Permissions.canUseThreads(props.betas) && !ReportUtils.isThreadFirstChat(props.action, props.report.reportID); + const shouldDisplayThreadReplies = hasReplies && props.action.childCommenterCount && !ReportUtils.isThreadFirstChat(props.action, props.report.reportID); const oldestFourEmails = lodashGet(props.action, 'childOldestFourEmails', '').split(','); return ( @@ -510,9 +503,6 @@ export default compose( preferredSkinTone: { key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, }, - betas: { - key: ONYXKEYS.BETAS, - }, }), )( memo( diff --git a/src/pages/home/report/ReportActionItemFragment.js b/src/pages/home/report/ReportActionItemFragment.js index 19fa6d6982e3..05cba64e6236 100644 --- a/src/pages/home/report/ReportActionItemFragment.js +++ b/src/pages/home/report/ReportActionItemFragment.js @@ -114,11 +114,13 @@ const ReportActionItemFragment = (props) => { return ${htmlContent}` : `${htmlContent}`} />; } + const containsOnlyEmojis = EmojiUtils.containsOnlyEmojis(text); + return ( {convertToLTR(Str.htmlDecode(text))} {Boolean(props.fragment.isEdited) && ( @@ -129,7 +131,7 @@ const ReportActionItemFragment = (props) => { > {' '} diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index b10134b2ceb7..3ec342b9cc2a 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -9,6 +9,7 @@ import * as ReportScrollManager from '../../../libs/ReportScrollManager'; import styles from '../../../styles/styles'; import * as ReportUtils from '../../../libs/ReportUtils'; import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions'; +import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes, withCurrentUserPersonalDetailsDefaultProps} from '../../../components/withCurrentUserPersonalDetails'; import {withNetwork, withPersonalDetails} from '../../../components/OnyxProvider'; import ReportActionItem from './ReportActionItem'; import ReportActionItemParentAction from './ReportActionItemParentAction'; @@ -55,6 +56,7 @@ const propTypes = { ...withDrawerPropTypes, ...windowDimensionsPropTypes, + ...withCurrentUserPersonalDetailsPropTypes, }; const defaultProps = { @@ -62,6 +64,7 @@ const defaultProps = { personalDetails: {}, mostRecentIOUReportActionID: '', isLoadingMoreReportActions: false, + ...withCurrentUserPersonalDetailsDefaultProps, }; /** @@ -146,7 +149,7 @@ const ReportActionsList = (props) => { // Native mobile does not render updates flatlist the changes even though component did update called. // To notify there something changes we can use extraData prop to flatlist const extraData = [!props.isDrawerOpen && props.isSmallScreenWidth ? props.newMarkerReportActionID : undefined, ReportUtils.isArchivedRoom(props.report)]; - const shouldShowReportRecipientLocalTime = ReportUtils.canShowReportRecipientLocalTime(props.personalDetails, props.report); + const shouldShowReportRecipientLocalTime = ReportUtils.canShowReportRecipientLocalTime(props.personalDetails, props.report, props.currentUserPersonalDetails.login); return ( 0 ? this.props.safeAreaPaddingBottomStyle : {}]}> - {this.props.translate('common.to')} User.clearContactMethodErrors(props.contactMethod, 'validateCodeSent')} > - + Navigation.dismissModal(true)} /> -
- - {props.translate('twoFactorAuth.codesLoseAccess')} - - - {props.account.isLoading ? ( - - - - ) : ( - <> - - {Boolean(props.account.recoveryCodes) && - _.map(props.account.recoveryCodes.split(', '), (code) => ( - - {code} - - ))} + +
+ + {props.translate('twoFactorAuth.codesLoseAccess')} + + + {props.account.isLoading ? ( + + - -
- -
+ +