diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 39c5a708e5db..4e7f01f38eb4 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -41,14 +41,18 @@ This is the most common scenario for contributors. The Expensify team posts new
#### Proposing a job that Expensify hasn’t posted
-It’s possible that you found a bug or enhancement that we haven’t posted to the [GitHub repository](https://github.com/Expensify/App/issues?q=is%3Aissue). This is an opportunity to propose a job, and (optionally) a solution. 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 improvement. 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. Please follow these steps to propose a job:
+It’s possible that you found a bug or enhancement that we haven’t posted to the [GitHub repository](https://github.com/Expensify/App/issues?q=is%3Aissue). This is an opportunity to propose a job, and (optionally) a solution. 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 improvement.
+- 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. Please follow these steps to propose a job:
+- Note about proposed improvements: Expensify has the right not to pay the $250 reward if the suggested improvement is already planned. Currently, Expensify plans to implement all features of the old Expensify app in New Expensify.
1. Check to ensure an issue does not already exist for this topic in the [New Expensify Issue list](https://github.com/Expensify/App/issues). Please use your best judgement by searching for similar titles and issue descriptions.
2. If your bug or enhancement matches an existing issue, please feel free to comment on that GitHub issue with your findings if you think it will help solve the issue.
-3. If there is no existing GitHub issue or Upwork job, report the issue(s) in the [#expensify-open-source](https://github.com/Expensify/App/blob/main/CONTRIBUTING.md#asking-questions) Slack channel, prefixed with `BUG:` or `Feature Request:`. Include all relevant data, screenshots and examples in the post or thread.
-4. After review in #expensify-open-source, 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:`
-5. 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. No additional work is needed. If the issue is fixed internally, a dedicated job will be created to hire and pay you after the issue is fixed.
-6. 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.
+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 production then report the issue(s) in the [#expensify-open-source](https://github.com/Expensify/App/blob/main/CONTRIBUTING.md#asking-questions) Slack channel, prefixed with `BUG:` or `Feature Request:`. Include all relevant data, screenshots and examples in the post or thread.
+7. After review in #expensify-open-source, 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. No additional work is needed. 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.
>**Note:** Our problem solving approach at Expensify is to focus on high value problems and avoid small optimizations with results that are difficult to measure. We also prefer to identify and solve problems at their root. Given that, please ensure all proposed jobs fix a specific problem in a measurable way with evidence so they are easy to evaluate. Here's an example of a good problem/solution:
>
@@ -89,7 +93,7 @@ It’s possible that you found a bug or enhancement that we haven’t posted to
```
9. [Open a pull request](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork), and make sure to fill in the required fields.
10. An Expensify engineer and a member from the Contributor-Plus team will be assigned to your pull request automatically to review.
-11. Provide daily updates until reaching completion of your PR.
+11. Daily updates on weekdays are highly recommended. If you know you won’t be able to provide updates for > 1 week, please comment on the PR or issue how long you plan to be out so that we may plan accordingly. We understand everyone needs a little vacation here and there. Any issue that doesn't receive an update for 1 full week may be considered abandoned and the original contract terminated.
#### Submit your pull request for final review
12. When you are ready to submit your pull request for final review, make sure the following checks pass:
diff --git a/android/app/build.gradle b/android/app/build.gradle
index fbfc463b3964..adf3538e2acf 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -149,8 +149,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1001012408
- versionName "1.1.24-8"
+ versionCode 1001012419
+ versionName "1.1.24-19"
}
splits {
abi {
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index ac5a3af7fdab..8abb6b5c92dd 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -31,7 +31,7 @@
CFBundleVersion
- 1.1.24.8
+ 1.1.24.19ITSAppUsesNonExemptEncryptionLSApplicationQueriesSchemes
diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist
index 0e8523e64536..0f4a43926052 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -19,6 +19,6 @@
CFBundleSignature????CFBundleVersion
- 1.1.24.8
+ 1.1.24.19
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index a8781483c7cc..397b254589e5 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -506,7 +506,7 @@ PODS:
- React-perflogger (= 0.64.1)
- rn-fetch-blob (0.12.0):
- React-Core
- - RNBootSplash (3.2.6):
+ - RNBootSplash (3.2.7):
- React-Core
- RNCAsyncStorage (1.15.5):
- React-Core
@@ -850,7 +850,7 @@ SPEC CHECKSUMS:
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
DoubleConversion: cf9b38bf0b2d048436d9a82ad2abe1404f11e7de
FBLazyVector: 7b423f9e248eae65987838148c36eec1dbfe0b53
- FBReactNativeSpec: 9f813f735901d719751da82f0365b5c506c28f14
+ FBReactNativeSpec: f5187ed83c38ec2fb4634140a81c0019adcd9cb3
Firebase: 54cdc8bc9c9b3de54f43dab86e62f5a76b47034f
FirebaseABTesting: c3e48ebf5e7e5c674c5a131c68e941d7921d83dc
FirebaseAnalytics: 4751d6a49598a2b58da678cc07df696bcd809ab9
@@ -920,7 +920,7 @@ SPEC CHECKSUMS:
React-runtimeexecutor: ff951a0c241bfaefc4940a3f1f1a229e7cb32fa6
ReactCommon: bedc99ed4dae329c4fcf128d0c31b9115e5365ca
rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba
- RNBootSplash: 00f99e3c05fb44af3613e6088406de4be0a8eca3
+ RNBootSplash: b82ee16a943903ea612edb15233ea4f247155ef9
RNCAsyncStorage: 8324611026e8dc3706f829953aa6e3899f581589
RNCClipboard: 5e299c6df8e0c98f3d7416b86ae563d3a9f768a3
RNCMaskedView: 138134c4d8a9421b4f2bf39055a79aa05c2d47b1
diff --git a/package-lock.json b/package-lock.json
index c12d0ec1feb4..7efaa63963cc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "1.1.24-8",
+ "version": "1.1.24-19",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -20752,12 +20752,18 @@
}
},
"domhandler": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz",
- "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
- "dev": true,
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz",
+ "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==",
"requires": {
- "domelementtype": "1"
+ "domelementtype": "^2.2.0"
+ },
+ "dependencies": {
+ "domelementtype": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
+ "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A=="
+ }
}
},
"dompurify": {
@@ -21578,10 +21584,9 @@
}
},
"entities": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
- "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
- "dev": true
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
+ "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q=="
},
"enumerate-devices": {
"version": "1.1.1",
@@ -25585,28 +25590,46 @@
}
},
"htmlparser2": {
- "version": "3.10.1",
- "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
- "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
- "dev": true,
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz",
+ "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==",
"requires": {
- "domelementtype": "^1.3.1",
- "domhandler": "^2.3.0",
- "domutils": "^1.5.1",
- "entities": "^1.1.1",
- "inherits": "^2.0.1",
- "readable-stream": "^3.1.1"
+ "domelementtype": "^2.0.1",
+ "domhandler": "^4.2.2",
+ "domutils": "^2.8.0",
+ "entities": "^3.0.1"
},
"dependencies": {
- "readable-stream": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
- "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
- "dev": true,
+ "dom-serializer": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
+ "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
"requires": {
- "inherits": "^2.0.3",
- "string_decoder": "^1.1.1",
- "util-deprecate": "^1.0.1"
+ "domelementtype": "^2.0.1",
+ "domhandler": "^4.2.0",
+ "entities": "^2.0.0"
+ },
+ "dependencies": {
+ "entities": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+ "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="
+ }
+ }
+ },
+ "domelementtype": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
+ "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A=="
+ },
+ "domutils": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+ "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+ "requires": {
+ "dom-serializer": "^1.0.1",
+ "domelementtype": "^2.2.0",
+ "domhandler": "^4.2.0"
}
}
}
@@ -36593,9 +36616,9 @@
}
},
"react-native-bootsplash": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/react-native-bootsplash/-/react-native-bootsplash-3.2.6.tgz",
- "integrity": "sha512-zNEXIe2K1A06J45QOAg+OBo3wIyId9lZXOwITUcwNR2bQEg/3CO6uvcRB7MLuy2ct54R1PlbADHDZK/Ozt7mfA==",
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/react-native-bootsplash/-/react-native-bootsplash-3.2.7.tgz",
+ "integrity": "sha512-jc46lBuKeZerdUndY0yJw2mIbvX0kMHpkKsayxqJfjCiMorklSPZt0dyvaM9KyebCVop54udMJAySbnZVCEvIQ==",
"requires": {
"chalk": "^4.1.2",
"fs-extra": "^10.0.0",
@@ -38116,6 +38139,46 @@
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true
},
+ "domhandler": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz",
+ "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "1"
+ }
+ },
+ "entities": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
+ "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
+ "dev": true
+ },
+ "htmlparser2": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
+ "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "^1.3.1",
+ "domhandler": "^2.3.0",
+ "domutils": "^1.5.1",
+ "entities": "^1.1.1",
+ "inherits": "^2.0.1",
+ "readable-stream": "^3.1.1"
+ }
+ },
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ },
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
diff --git a/package.json b/package.json
index a333d398d2f6..1dcdd3b47e9a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "1.1.24-8",
+ "version": "1.1.24-19",
"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.",
@@ -57,6 +57,8 @@
"@react-navigation/native": "6.0.5",
"@react-navigation/stack": "6.0.10",
"babel-plugin-transform-remove-console": "^6.9.4",
+ "dom-serializer": "^0.2.2",
+ "domhandler": "^4.3.0",
"dotenv": "^8.2.0",
"electron-context-menu": "^2.3.0",
"electron-log": "^4.3.5",
@@ -65,6 +67,7 @@
"expensify-common": "git+https://github.com/Expensify/expensify-common.git#fa190f6c844cf5646345f3e5e4862b62f1fa27bc",
"file-loader": "^6.0.0",
"html-entities": "^1.3.1",
+ "htmlparser2": "^7.2.0",
"lodash": "4.17.21",
"metro-config": "^0.64.0",
"moment": "^2.27.0",
@@ -76,7 +79,7 @@
"react-collapse": "^5.1.0",
"react-dom": "^17.0.2",
"react-native": "0.64.1",
- "react-native-bootsplash": "^3.2.6",
+ "react-native-bootsplash": "^3.2.7",
"react-native-collapsible": "^1.6.0",
"react-native-config": "^1.4.0",
"react-native-document-picker": "^5.1.0",
diff --git a/src/CONST.js b/src/CONST.js
index 6bcac4aad78e..04418dd9ed52 100755
--- a/src/CONST.js
+++ b/src/CONST.js
@@ -161,6 +161,11 @@ const CONST = {
shortcutKey: 'Enter',
modifiers: [],
},
+ COPY: {
+ descriptionKey: 'copy',
+ shortcutKey: 'C',
+ modifiers: ['CTRL'],
+ },
},
KEYBOARD_SHORTCUT_KEY_DISPLAY_NAME: {
CONTROL: 'Ctrl',
diff --git a/src/components/PressableWithSecondaryInteraction/index.js b/src/components/PressableWithSecondaryInteraction/index.js
index c14d0576a5aa..78434ae65476 100644
--- a/src/components/PressableWithSecondaryInteraction/index.js
+++ b/src/components/PressableWithSecondaryInteraction/index.js
@@ -1,6 +1,7 @@
import _ from 'underscore';
import React, {Component} from 'react';
import {Pressable} from 'react-native';
+import SelectionScraper from '../../libs/SelectionScraper';
import * as pressableWithSecondaryInteractionPropTypes from './pressableWithSecondaryInteractionPropTypes';
import styles from '../../styles/styles';
@@ -30,7 +31,7 @@ class PressableWithSecondaryInteraction extends Component {
* https://developer.mozilla.org/en-US/docs/Web/API/Element/contextmenu_event
*/
executeSecondaryInteractionOnContextMenu(e) {
- const selection = window.getSelection().toString();
+ const selection = SelectionScraper.getAsMarkdown();
e.stopPropagation();
if (this.props.preventDefaultContentMenu) {
e.preventDefault();
diff --git a/src/components/WelcomeText.js b/src/components/WelcomeText.js
index 1180b5df2e3a..0624f4061fbc 100755
--- a/src/components/WelcomeText.js
+++ b/src/components/WelcomeText.js
@@ -21,7 +21,7 @@ const WelcomeText = (props) => {
return (
<>
- {props.translate('welcomeText.phrase1')}
+ {props.translate('welcomeText.welcome')}
{props.translate('welcomeText.phrase2')}
diff --git a/src/languages/en.js b/src/languages/en.js
index 8c0bd1eed0e6..8a5ed034202d 100755
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -136,10 +136,10 @@ export default {
hello: 'Hello',
phoneCountryCode: '1',
welcomeText: {
- phrase1: 'Welcome to the New Expensify! Enter your phone number or email to continue.',
+ welcome: 'Welcome to the New Expensify! Enter your phone number or email to continue.',
phrase2: 'Money talks. And now that chat and payments are in one place, it\'s also easy.',
phrase3: 'Your payments get to you as fast as you can get your point across.',
- phrase4: 'Welcome back to the New Expensify! Please enter your password.',
+ welcomeBack: 'Welcome back to the New Expensify! Please enter your password.',
},
reportActionCompose: {
addAction: 'Actions',
@@ -275,7 +275,7 @@ export default {
label: 'iOS',
},
desktop: {
- label: 'Desktop',
+ label: 'macOS',
},
},
security: 'Security',
@@ -290,14 +290,14 @@ export default {
},
passwordPage: {
changePassword: 'Change password',
- changingYourPasswordPrompt: 'Changing your password will update your password for both your Expensify.com\nand New Expensify accounts.',
+ changingYourPasswordPrompt: 'Changing your password will update your password for both your Expensify.com and New Expensify accounts.',
currentPassword: 'Current password',
newPassword: 'New password',
- newPasswordPrompt: 'New password must be different than your old password, have at least 8 characters,\n1 capital letter, 1 lowercase letter, and 1 number.',
+ newPasswordPrompt: 'New password must be different than your old password, have at least 8 characters, 1 capital letter, 1 lowercase letter, and 1 number.',
errors: {
currentPassword: 'Current password is required',
newPasswordSameAsOld: 'New password must be different than your old password',
- newPassword: 'Your password must have at least 8 characters,\n1 capital letter, 1 lowercase letter, and 1 number.',
+ newPassword: 'Your password must have at least 8 characters, 1 capital letter, 1 lowercase letter, and 1 number.',
},
},
addPayPalMePage: {
@@ -428,6 +428,7 @@ export default {
linkHasBeenResent: 'Link has been re-sent',
weSentYouMagicSignInLink: ({login}) => `We've sent a magic sign in link to ${login}. Check your Inbox and your Spam folder and wait 5-10 minutes before trying again.`,
resendLink: 'Resend link',
+ validationCodeFailedMessage: 'It looks like there was an error with your validation link or it has expired.',
unvalidatedAccount: 'This account exists but isn\'t validated, please check your inbox for your magic link.',
newAccount: ({login, loginType}) => `Welcome ${login}, it's always great to see a new face around here! Please check your ${loginType} for a magic link to validate your account.`,
},
@@ -445,7 +446,7 @@ export default {
setPasswordPage: {
enterPassword: 'Enter a password',
setPassword: 'Set password',
- newPasswordPrompt: 'Your password must have at least 8 characters,\n1 capital letter, 1 lowercase letter, and 1 number.',
+ newPasswordPrompt: 'Your password must have at least 8 characters, 1 capital letter, 1 lowercase letter, and 1 number.',
passwordFormTitle: 'Welcome back to the New Expensify! Please set your password.',
passwordNotSet: 'We were unable to set your new password correctly.',
accountNotValidated: 'We were unable to validate your account. The validation code may have expired.',
@@ -840,6 +841,7 @@ export default {
escape: 'Escape Dialogs',
search: 'Open search dialog',
newGroup: 'New group screen',
+ copy: 'Copy comment',
},
},
guides: {
diff --git a/src/languages/es.js b/src/languages/es.js
index 4f20b42f3330..e9ee7554db85 100644
--- a/src/languages/es.js
+++ b/src/languages/es.js
@@ -136,10 +136,10 @@ export default {
hello: 'Hola',
phoneCountryCode: '34',
welcomeText: {
- phrase1: 'Con el Nuevo Expensify, chat y pagos son lo mismo.',
+ welcome: 'Con el Nuevo Expensify, chat y pagos son lo mismo.',
phrase2: 'El dinero habla. Y ahora que chat y pagos están en un mismo lugar, es también fácil.',
phrase3: 'Tus pagos llegan tan rápido como tus mensajes.',
- phrase4: '¡Bienvenido de vuelta al Nuevo Expensify! Por favor, introduce tu contraseña.',
+ welcomeBack: '¡Bienvenido de vuelta al Nuevo Expensify! Por favor, introduce tu contraseña.',
},
reportActionCompose: {
addAction: 'Acción',
@@ -275,7 +275,7 @@ export default {
label: 'iOS',
},
desktop: {
- label: 'Desktop',
+ label: 'macOS',
},
},
security: 'Seguridad',
@@ -290,14 +290,14 @@ export default {
},
passwordPage: {
changePassword: 'Cambiar contraseña',
- changingYourPasswordPrompt: 'El cambio de contraseña va a afectar tanto a la cuenta de Expensify.com\ncomo la de Nuevo Expensify.',
+ changingYourPasswordPrompt: 'El cambio de contraseña va a afectar tanto a la cuenta de Expensify.com como la de Nuevo Expensify.',
currentPassword: 'Contraseña actual',
newPassword: 'Nueva contraseña',
- newPasswordPrompt: 'La nueva contraseña debe ser diferente de la antigua, tener al menos 8 caracteres,\n1 letra mayúscula, 1 letra minúscula y 1 número.',
+ newPasswordPrompt: 'La nueva contraseña debe ser diferente de la antigua, tener al menos 8 caracteres, 1 letra mayúscula, 1 letra minúscula y 1 número.',
errors: {
currentPassword: 'Contraseña actual es requerido',
newPasswordSameAsOld: 'La nueva contraseña tiene que ser diferente de la antigua',
- newPassword: 'Su contraseña debe tener al menos 8 caracteres, \n1 letra mayúscula, 1 letra minúscula y 1 número.',
+ newPassword: 'Su contraseña debe tener al menos 8 caracteres, 1 letra mayúscula, 1 letra minúscula y 1 número.',
},
},
addPayPalMePage: {
@@ -428,6 +428,7 @@ export default {
linkHasBeenResent: 'El enlace se ha reenviado',
weSentYouMagicSignInLink: ({login}) => `Hemos enviado un enlace mágico de inicio de sesión a ${login}. Verifica tu bandeja de entrada y tu carpeta de correo no deseado y espera de 5 a 10 minutos antes de intentarlo de nuevo.`,
resendLink: 'Reenviar enlace',
+ validationCodeFailedMessage: 'Parece que hubo un error con el enlace de validación o ha caducado.',
unvalidatedAccount: 'Esta cuenta existe pero no está validada, por favor busca el enlace mágico en tu bandeja de entrada',
newAccount: ({login, loginType}) => `¡Bienvenido ${login}, es genial ver una cara nueva por aquí! En tu ${loginType} encontrarás un enlace para validar tu cuenta, por favor, revísalo`,
},
@@ -445,7 +446,7 @@ export default {
setPasswordPage: {
enterPassword: 'Escribe una contraseña',
setPassword: 'Configura tu contraseña',
- newPasswordPrompt: 'La contraseña debe tener al menos 8 caracteres, \n1 letra mayúscula, 1 letra minúscula y 1 número.',
+ newPasswordPrompt: 'La contraseña debe tener al menos 8 caracteres, 1 letra mayúscula, 1 letra minúscula y 1 número.',
passwordFormTitle: '¡Bienvenido de vuelta al Nuevo Expensify! Por favor, elige una contraseña.',
passwordNotSet: 'No pudimos establecer to contaseña correctamente.',
accountNotValidated: 'No pudimos validar tu cuenta. Es posible que el enlace de validación haya caducado.',
@@ -842,6 +843,7 @@ export default {
escape: 'Diálogos de escape',
search: 'Abrir diálogo de búsqueda',
newGroup: 'Nueva pantalla de grupo',
+ copy: 'Copiar comentario',
},
},
guides: {
diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js
index e8c17a932ef9..58e57c892d9a 100644
--- a/src/libs/OptionsListUtils.js
+++ b/src/libs/OptionsListUtils.js
@@ -583,7 +583,7 @@ function getSearchOptions(
) {
return getOptions(reports, personalDetails, 0, {
betas,
- searchValue,
+ searchValue: searchValue.trim(),
includeRecentReports: true,
includeMultipleParticipantReports: true,
maxRecentReportsToShow: 0, // Unlimited
@@ -652,7 +652,7 @@ function getNewChatOptions(
) {
return getOptions(reports, personalDetails, 0, {
betas,
- searchValue,
+ searchValue: searchValue.trim(),
selectedOptions,
excludeDefaultRooms: true,
includeRecentReports: true,
diff --git a/src/libs/SelectionScraper/index.js b/src/libs/SelectionScraper/index.js
new file mode 100644
index 000000000000..07fdb2ca490c
--- /dev/null
+++ b/src/libs/SelectionScraper/index.js
@@ -0,0 +1,142 @@
+import render from 'dom-serializer';
+import ExpensiMark from 'expensify-common/lib/ExpensiMark';
+import {parseDocument} from 'htmlparser2';
+import {Element} from 'domhandler';
+import _ from 'underscore';
+
+const elementsWillBeSkipped = ['html', 'body'];
+const tagAttribute = 'data-testid';
+
+/**
+ * Reads html of selection. If browser doesn't support Selection API, returns empty string.
+ * @returns {String} HTML of selection as String
+ */
+const getHTMLOfSelection = () => {
+ if (window.getSelection) {
+ const selection = window.getSelection();
+
+ if (selection.rangeCount > 0) {
+ const div = document.createElement('div');
+
+ // HTML tag of markdown comments is in data-testid attribute (em, strong, blockquote..). Our goal here is to
+ // find that nodes and replace that tag with the one inside data-testid, so ExpensiMark can parse it.
+ // Simply, we want to replace this:
+ // bold
+ // to this:
+ // bold
+ //
+ // We traverse all ranges, and get closest node with data-testid and replace its contents with contents of
+ // range.
+ for (let i = 0; i < selection.rangeCount; i++) {
+ const range = selection.getRangeAt(i);
+
+ const clonedSelection = range.cloneContents();
+
+ // If clonedSelection has no text content this data has no meaning to us.
+ if (clonedSelection.textContent) {
+ let node = null;
+
+ // If selection starts and ends within same text node we use its parentNode. This is because we can't
+ // use closest function on a [Text](https://developer.mozilla.org/en-US/docs/Web/API/Text) node.
+ // We are selecting closest node because nodes with data-testid can be one of the parents of the actual node.
+ // Assuming we selected only "block" part of following html:
+ //