diff --git a/.gitignore b/.gitignore
index b3f18d309e6..e03ba726246 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,6 +33,7 @@ build/
local.properties
*.iml
android/*.hprof
+android/app/src/main/java/com/expensify/chat/generated/
# Vscode
.vscode
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 3011237b111..52a02d50173 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -150,8 +150,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1001008301
- versionName "1.0.83-1"
+ versionCode 1001008302
+ versionName "1.0.83-2"
}
splits {
abi {
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 6976c0f8c82..199dc57042f 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -55,7 +55,7 @@
-
+
diff --git a/ios/ExpensifyCash/Chat.entitlements b/ios/ExpensifyCash/Chat.entitlements
index 2f8be2ab354..98e887b4423 100644
--- a/ios/ExpensifyCash/Chat.entitlements
+++ b/ios/ExpensifyCash/Chat.entitlements
@@ -6,9 +6,8 @@
development
com.apple.developer.associated-domains
- applinks:www.expensify.cash
- applinks:staging.expensify.cash
- applinks:expensify.cash
+ applinks:staging.new.expensify.com
+ applinks:new.expensify.com
diff --git a/ios/ExpensifyCash/Info.plist b/ios/ExpensifyCash/Info.plist
index bb9717379d1..ae4f4674d82 100644
--- a/ios/ExpensifyCash/Info.plist
+++ b/ios/ExpensifyCash/Info.plist
@@ -30,7 +30,7 @@
CFBundleVersion
- 1.0.83.1
+ 1.0.83.2
ITSAppUsesNonExemptEncryption
LSApplicationQueriesSchemes
diff --git a/ios/ExpensifyCashTests/Info.plist b/ios/ExpensifyCashTests/Info.plist
index 88bbe484331..8de36bb3036 100644
--- a/ios/ExpensifyCashTests/Info.plist
+++ b/ios/ExpensifyCashTests/Info.plist
@@ -19,6 +19,6 @@
CFBundleSignature
????
CFBundleVersion
- 1.0.83.1
+ 1.0.83.2
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 266fa84eaa6..597a1d8fef3 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -872,7 +872,7 @@ SPEC CHECKSUMS:
DoubleConversion: cf9b38bf0b2d048436d9a82ad2abe1404f11e7de
EXHaptics: 337c160c148baa6f0e7166249f368965906e346b
FBLazyVector: 7b423f9e248eae65987838148c36eec1dbfe0b53
- FBReactNativeSpec: 825b0f0851f5cc5c6268a920286281f62fc96c37
+ FBReactNativeSpec: c783a75db87c963c60afcd461fc38358805fe5ba
Firebase: 54cdc8bc9c9b3de54f43dab86e62f5a76b47034f
FirebaseABTesting: 4cb61aeeb50f60680af1c01fff781dfaf9293916
FirebaseAnalytics: 4751d6a49598a2b58da678cc07df696bcd809ab9
@@ -899,9 +899,9 @@ SPEC CHECKSUMS:
Onfido: 116a268e4cb8b767c15285e8071c2e8304673cdf
onfido-react-native-sdk: b8f1b7cbe1adab6479d735275772390161630dcd
OpenSSL-Universal: 1aa4f6a6ee7256b83db99ec1ccdaa80d10f9af9b
- Permission-LocationAccuracy: 76669f87b4c276f5ae803cc0ddd1862a4c0e9dd8
- Permission-LocationAlways: a274bc04bb386068782468dbdaca3859f51634ca
- Permission-LocationWhenInUse: 3a2b0dbc167d79e8e920a4377ff9520cdc108407
+ Permission-LocationAccuracy: e8adff9ede1b23b43b7054a4500113d515fc87a8
+ Permission-LocationAlways: 7f7f373d086af7a81b2f4f20d65d29266ca2043b
+ Permission-LocationWhenInUse: 3ae82a9feb5da4e94e386dba17c7dd3531af9feb
Plaid: f55c6acdc249245c6778a4045757eb4e839cca61
PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58
Protobuf: 7327d4444215b5f18e560a97f879ff5503c4581c
@@ -917,15 +917,15 @@ SPEC CHECKSUMS:
React-jsiexecutor: 124e8f99992490d0d13e0649d950d3e1aae06fe9
React-jsinspector: 500a59626037be5b3b3d89c5151bc3baa9abf1a9
react-native-config: d8b45133fd13d4f23bd2064b72f6e2c08b2763ed
- react-native-document-picker: f2f73db94328c84e22144e369fb4a3ede47bc1f5
+ react-native-document-picker: 0e3602a4064da040321bafad6848d8b0edcb1d55
react-native-flipper: 1943b82f2e494c77b741eb1ed257b6734a334b83
- react-native-image-picker: 474cf2c33c2b6671da53d293a16c97995f0aec15
- react-native-netinfo: 30fb89fa913c342be82a887b56e96be6d71201dd
+ react-native-image-picker: 4089335b89b625d4e34d53fb249c48a7a791b3ea
+ react-native-netinfo: 52cf0ee8342548a485e28f4b09e56b477567244d
react-native-pdf: 4b5a9e4465a6a3b399e91dc4838eb44ddf716d1f
- react-native-plaid-link-sdk: 59b7376efca9f00e9693321c5cf7c6ab2c567635
- react-native-progress-bar-android: be43138ab7da30d51fc038bafa98e9ed594d0c40
- react-native-progress-view: 21b1e29e70c7559c16c9e0a04c4adc19fce6ede2
- react-native-safe-area-context: 79fea126c6830c85f65947c223a5e3058a666937
+ react-native-plaid-link-sdk: 1a6593e2d3d790e8113c29178d883eb883f8c032
+ react-native-progress-bar-android: ce95a69f11ac580799021633071368d08aaf9ad8
+ react-native-progress-view: 5816e8a6be812c2b122c6225a2a3db82d9008640
+ react-native-safe-area-context: 01158a92c300895d79dee447e980672dc3fb85a6
React-perflogger: aad6d4b4a267936b3667260d1f649b6f6069a675
React-RCTActionSheet: fc376be462c9c8d6ad82c0905442fd77f82a9d2a
React-RCTAnimation: ba0a1c3a2738be224a08092fa7f1b444ab77d309
@@ -939,17 +939,17 @@ SPEC CHECKSUMS:
React-runtimeexecutor: ff951a0c241bfaefc4940a3f1f1a229e7cb32fa6
ReactCommon: bedc99ed4dae329c4fcf128d0c31b9115e5365ca
rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba
- RNBootSplash: 24175aa28fe203b10c48dc34e78d946fd33c77af
+ RNBootSplash: 3123ba68fe44d8be09a014e89cc8f0f55b68a521
RNCAsyncStorage: 8324611026e8dc3706f829953aa6e3899f581589
- RNCClipboard: 41d8d918092ae8e676f18adada19104fa3e68495
- RNCMaskedView: fc29d354a40316a990e8fb46391f08aea829c3aa
+ RNCClipboard: 5e299c6df8e0c98f3d7416b86ae563d3a9f768a3
+ RNCMaskedView: 138134c4d8a9421b4f2bf39055a79aa05c2d47b1
RNCPicker: 6780c753e9e674065db90d9c965920516402579d
RNFBAnalytics: 8ba84c2d31c64374d054c8621b998f25145ffddc
RNFBApp: 64c90ab78b6010ed5c3ade026dfe5ff6442c21fd
RNFBCrashlytics: 1de18b8cc36d9bcf86407c4a354399228cc84a61
RNFBPerf: e3a7269f573a4787810a32de51647cdcbe08dfb4
RNGestureHandler: 9b7e605a741412e20e13c512738a31bd1611759b
- RNPermissions: 4c8a37b4dde50f1f152bf8cd08c4a43d2355829e
+ RNPermissions: eb94f9fdc0a8ecd02fcce0676d56ffb1395d41e1
RNReanimated: 833ebd229b31e18a8933ebd0cd744a0f47d88c42
RNScreens: e8e8dd0588b5da0ab57dcca76ab9b2d8987757e0
RNSVG: ce9d996113475209013317e48b05c21ee988d42e
@@ -966,7 +966,7 @@ SPEC CHECKSUMS:
UMReactNativeAdapter: 7b458ca3d4497b5114e6bb766b223432bad22d8a
UMSensorsInterface: 50439b47826e716a514cbd7384aebe9ab4fde5f4
UMTaskManagerInterface: 482155764886069beb1bc7fcf6036f12e4ad0751
- urbanairship-react-native: a05a913d6f9559141d477ff4d380bcd616b5c59d
+ urbanairship-react-native: d415a12e67ba93bf3ce914df9a310b66a88a5cc3
Yoga: a7de31c64fe738607e7a3803e3f591a4b1df7393
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
diff --git a/package-lock.json b/package-lock.json
index 9aa4ff70837..4c9c96b5b30 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "expensify.cash",
- "version": "1.0.83-1",
+ "version": "1.0.83-2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -23084,8 +23084,8 @@
}
},
"expensify-common": {
- "version": "git://github.com/Expensify/expensify-common.git#4016a786d66ff38ed76a191edef31ebd737a0190",
- "from": "git://github.com/Expensify/expensify-common.git#4016a786d66ff38ed76a191edef31ebd737a0190",
+ "version": "git://github.com/Expensify/expensify-common.git#8f286b5826a041ecb739cff1ad6940ffb9324bee",
+ "from": "git://github.com/Expensify/expensify-common.git#8f286b5826a041ecb739cff1ad6940ffb9324bee",
"requires": {
"classnames": "2.3.1",
"clipboard": "2.0.4",
@@ -23105,11 +23105,6 @@
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
"integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
},
- "jquery": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
- "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw=="
- },
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@@ -29605,6 +29600,11 @@
"resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.2.tgz",
"integrity": "sha512-+az2gi/hvex7eLTMTlbRLOhH6P6WFdk2ITI8HJsaH2VqYO0I594zXSYEP+tf4FW+8Cy68ScDXoAsQdyQanv3sw=="
},
+ "jquery": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
+ "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw=="
+ },
"js-string-escape": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz",
@@ -36094,56 +36094,43 @@
}
},
"react-native-onyx": {
- "version": "git+https://github.com/Expensify/react-native-onyx.git#d73900b7cb7bf82bed77cb6b6baabf8fe2eb3a0e",
- "from": "git+https://github.com/Expensify/react-native-onyx.git#d73900b7cb7bf82bed77cb6b6baabf8fe2eb3a0e",
+ "version": "git+https://github.com/Expensify/react-native-onyx.git#d7553b95e982ab78f6bb2064f6b0549f0ace94c2",
+ "from": "git+https://github.com/Expensify/react-native-onyx.git#d7553b95e982ab78f6bb2064f6b0549f0ace94c2",
"requires": {
"ascii-table": "0.0.9",
- "expensify-common": "git+https://github.com/Expensify/expensify-common.git#4016a786d66ff38ed76a191edef31ebd737a0190",
+ "expensify-common": "git+https://github.com/Expensify/expensify-common.git#2e5cff552cf132da90a3fb9756e6b4fb6ae7b40c",
"lodash": "4.17.21",
"underscore": "^1.13.1"
},
"dependencies": {
- "classnames": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
- "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
- },
"expensify-common": {
- "version": "git+https://github.com/Expensify/expensify-common.git#4016a786d66ff38ed76a191edef31ebd737a0190",
- "from": "git+https://github.com/Expensify/expensify-common.git#4016a786d66ff38ed76a191edef31ebd737a0190",
+ "version": "git+https://github.com/Expensify/expensify-common.git#2e5cff552cf132da90a3fb9756e6b4fb6ae7b40c",
+ "from": "git+https://github.com/Expensify/expensify-common.git#2e5cff552cf132da90a3fb9756e6b4fb6ae7b40c",
"requires": {
- "classnames": "2.3.1",
+ "classnames": "2.2.5",
"clipboard": "2.0.4",
"html-entities": "^1.3.1",
- "jquery": "3.6.0",
+ "jquery": "3.3.1",
"lodash": "4.17.21",
"prop-types": "15.7.2",
"react": "16.12.0",
"react-dom": "16.12.0",
- "semver": "^7.3.5",
+ "semver": "^7.3.4",
"simply-deferred": "git+https://github.com/Expensify/simply-deferred.git#77a08a95754660c7bd6e0b6979fdf84e8e831bf5",
- "underscore": "1.13.1"
+ "underscore": "1.9.1"
},
"dependencies": {
"underscore": {
- "version": "1.13.1",
- "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz",
- "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g=="
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz",
+ "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg=="
}
}
},
"jquery": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
- "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw=="
- },
- "lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "requires": {
- "yallist": "^4.0.0"
- }
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz",
+ "integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg=="
},
"react": {
"version": "16.12.0",
@@ -36166,23 +36153,10 @@
"scheduler": "^0.18.0"
}
},
- "semver": {
- "version": "7.3.5",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
- "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
- "requires": {
- "lru-cache": "^6.0.0"
- }
- },
"underscore": {
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz",
"integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g=="
- },
- "yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
}
}
},
diff --git a/package.json b/package.json
index ea9a248c11e..69c3a6b1581 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "expensify.cash",
- "version": "1.0.83-1",
+ "version": "1.0.83-2",
"author": "Expensify, Inc.",
"homepage": "https://new.expensify.com",
"description": "Expensify.cash is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
@@ -60,7 +60,7 @@
"electron-log": "^4.3.5",
"electron-serve": "^1.0.0",
"electron-updater": "^4.3.4",
- "expensify-common": "git://github.com/Expensify/expensify-common.git#4016a786d66ff38ed76a191edef31ebd737a0190",
+ "expensify-common": "git://github.com/Expensify/expensify-common.git#8f286b5826a041ecb739cff1ad6940ffb9324bee",
"expo-haptics": "^10.0.0",
"file-loader": "^6.0.0",
"html-entities": "^1.3.1",
@@ -84,7 +84,7 @@
"react-native-image-picker": "^4.0.3",
"react-native-keyboard-spacer": "^0.4.1",
"react-native-modal": "^11.10.0",
- "react-native-onyx": "git+https://github.com/Expensify/react-native-onyx.git#d73900b7cb7bf82bed77cb6b6baabf8fe2eb3a0e",
+ "react-native-onyx": "git+https://github.com/Expensify/react-native-onyx.git#d7553b95e982ab78f6bb2064f6b0549f0ace94c2",
"react-native-pdf": "^6.2.2",
"react-native-permissions": "^3.0.1",
"react-native-picker-select": "8.0.4",
diff --git a/src/CONST.js b/src/CONST.js
index da45292973d..51b4c772020 100755
--- a/src/CONST.js
+++ b/src/CONST.js
@@ -122,7 +122,7 @@ const CONST = {
NEW_GOOGLE_MEET_MEETING_URL: 'https://meet.google.com/new',
PDF_VIEWER_URL: '/pdf/web/viewer.html',
EXPENSIFY_ICON_URL: `${CLOUDFRONT_URL}/images/favicon-2019.png`,
- UPWORK_URL: 'https://www.upwork.com/ab/jobs/search/?q=Expensify%20React%20Native&user_location_match=2',
+ UPWORK_URL: 'https://github.com/Expensify/App/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22',
GITHUB_URL: 'https://github.com/Expensify/Expensify.cash',
TERMS_URL: 'https://use.expensify.com/terms',
PRIVACY_URL: 'https://use.expensify.com/privacy',
@@ -343,6 +343,7 @@ const CONST = {
ERROR: 'error',
WARNING: 'warning',
DURATION: 2000,
+ DURATION_LONG: 3500,
},
DEFAULT_LOCALE: 'en',
diff --git a/src/components/AddPlaidBankAccount.js b/src/components/AddPlaidBankAccount.js
index 8c1829e7910..995d83607b6 100644
--- a/src/components/AddPlaidBankAccount.js
+++ b/src/components/AddPlaidBankAccount.js
@@ -3,7 +3,6 @@ import React from 'react';
import {
ActivityIndicator,
View,
- TextInput,
} from 'react-native';
import PropTypes from 'prop-types';
import lodashGet from 'lodash/get';
@@ -20,8 +19,9 @@ import canFocusInputOnScreenFocus from '../libs/canFocusInputOnScreenFocus';
import compose from '../libs/compose';
import withLocalize, {withLocalizePropTypes} from './withLocalize';
import Button from './Button';
-import Picker from './Picker';
+import ExpensiPicker from './ExpensiPicker';
import Text from './Text';
+import ExpensiTextInput from './ExpensiTextInput';
const propTypes = {
...withLocalizePropTypes,
@@ -161,7 +161,7 @@ class AddPlaidBankAccount extends React.Component {
https://d2k5nsl2zxldvw.cloudfront.net/images/plaid/bg_plaidLogos_12@2x.png */}
{this.state.institution.name}
- {
this.setState({selectedIndex: Number(index)});
}}
@@ -175,12 +175,9 @@ class AddPlaidBankAccount extends React.Component {
{!_.isUndefined(this.state.selectedIndex) && (
-
- {this.props.translate('addPersonalBankAccountPage.enterPassword')}
-
- ;
+
+export default CardOverlay;
diff --git a/src/components/ExpensiPicker.js b/src/components/ExpensiPicker.js
new file mode 100644
index 00000000000..c8074414629
--- /dev/null
+++ b/src/components/ExpensiPicker.js
@@ -0,0 +1,58 @@
+import React, {PureComponent} from 'react';
+import {Text, View} from 'react-native';
+import PropTypes from 'prop-types';
+import Picker from './Picker';
+import styles from '../styles/styles';
+
+const propTypes = {
+ /** Picker label */
+ label: PropTypes.string,
+
+ /** Should the picker appear disabled? */
+ isDisabled: PropTypes.bool,
+};
+
+const defaultProps = {
+ label: '',
+ isDisabled: false,
+};
+
+class ExpensiPicker extends PureComponent {
+ constructor() {
+ super();
+ this.state = {
+ isOpen: false,
+ };
+ }
+
+ render() {
+ const {
+ label, isDisabled, ...pickerProps
+ } = this.props;
+ return (
+
+ {label && (
+ {label}
+ )}
+ this.setState({isOpen: true})}
+ onClose={() => this.setState({isOpen: false})}
+ disabled={isDisabled}
+ // eslint-disable-next-line react/jsx-props-no-spreading
+ {...pickerProps}
+ />
+
+ );
+ }
+}
+
+ExpensiPicker.propTypes = propTypes;
+ExpensiPicker.defaultProps = defaultProps;
+
+export default ExpensiPicker;
diff --git a/src/components/ExpensiTextInput/BaseExpensiTextInput.js b/src/components/ExpensiTextInput/BaseExpensiTextInput.js
new file mode 100644
index 00000000000..7194a35fe27
--- /dev/null
+++ b/src/components/ExpensiTextInput/BaseExpensiTextInput.js
@@ -0,0 +1,139 @@
+import React, {Component} from 'react';
+import {
+ Animated, TextInput, View, TouchableWithoutFeedback,
+} from 'react-native';
+import ExpensiTextInputLabel from './ExpensiTextInputLabel';
+import {propTypes, defaultProps} from './propTypes';
+import themeColors from '../../styles/themes/default';
+import styles from '../../styles/styles';
+
+const ACTIVE_LABEL_TRANSLATE_Y = -10;
+const ACTIVE_LABEL_TRANSLATE_X = (translateX = -22) => translateX;
+const ACTIVE_LABEL_SCALE = 0.8668;
+
+const INACTIVE_LABEL_TRANSLATE_Y = 0;
+const INACTIVE_LABEL_TRANSLATE_X = 0;
+const INACTIVE_LABEL_SCALE = 1;
+
+class BaseExpensiTextInput extends Component {
+ constructor(props) {
+ super(props);
+
+ const hasValue = props.value.length > 0;
+
+ this.state = {
+ isFocused: false,
+ labelTranslateY: new Animated.Value(hasValue ? ACTIVE_LABEL_TRANSLATE_Y : INACTIVE_LABEL_TRANSLATE_Y),
+ labelTranslateX: new Animated.Value(hasValue
+ ? ACTIVE_LABEL_TRANSLATE_X(props.translateX) : INACTIVE_LABEL_TRANSLATE_X),
+ labelScale: new Animated.Value(hasValue ? ACTIVE_LABEL_SCALE : INACTIVE_LABEL_SCALE),
+ };
+
+ this.input = null;
+ this.onFocus = this.onFocus.bind(this);
+ this.onBlur = this.onBlur.bind(this);
+ }
+
+ onFocus() {
+ if (this.props.onFocus) { this.props.onFocus(); }
+ this.setState({isFocused: true});
+ if (this.props.value.length === 0) {
+ this.animateLabel(
+ ACTIVE_LABEL_TRANSLATE_Y,
+ ACTIVE_LABEL_TRANSLATE_X(this.props.translateX),
+ ACTIVE_LABEL_SCALE,
+ );
+ }
+ }
+
+ onBlur() {
+ if (this.props.onBlur) { this.props.onBlur(); }
+ this.setState({isFocused: false});
+ if (this.props.value.length === 0) {
+ this.animateLabel(INACTIVE_LABEL_TRANSLATE_Y, INACTIVE_LABEL_TRANSLATE_X, INACTIVE_LABEL_SCALE);
+ }
+ }
+
+ animateLabel(translateY, translateX, scale) {
+ Animated.parallel([
+ Animated.spring(this.state.labelTranslateY, {
+ toValue: translateY,
+ duration: 80,
+ useNativeDriver: true,
+ }),
+ Animated.spring(this.state.labelTranslateX, {
+ toValue: translateX,
+ duration: 80,
+ useNativeDriver: true,
+ }),
+ Animated.spring(this.state.labelScale, {
+ toValue: scale,
+ duration: 80,
+ useNativeDriver: true,
+ }),
+ ]).start();
+ }
+
+ render() {
+ const {
+ label,
+ value,
+ placeholder,
+ hasError,
+ containerStyles,
+ inputStyle,
+ ignoreLabelTranslateX,
+ innerRef,
+ ...inputProps
+ } = this.props;
+
+ const hasLabel = Boolean(label.length);
+ return (
+
+ this.input.focus()}>
+
+ {hasLabel ? (
+
+ ) : null}
+ {
+ if (typeof innerRef === 'function') { innerRef(ref); }
+ this.input = ref;
+ }}
+ // eslint-disable-next-line
+ {...inputProps}
+ value={value}
+ placeholder={(this.state.isFocused || !label) ? placeholder : null}
+ placeholderTextColor={themeColors.placeholderText}
+ underlineColorAndroid="transparent"
+ style={inputStyle}
+ onFocus={this.onFocus}
+ onBlur={this.onBlur}
+ />
+
+
+
+ );
+ }
+}
+
+BaseExpensiTextInput.propTypes = propTypes;
+BaseExpensiTextInput.defaultProps = defaultProps;
+
+export default BaseExpensiTextInput;
diff --git a/src/components/ExpensiTextInput/ExpensiTextInputLabel/index.js b/src/components/ExpensiTextInput/ExpensiTextInputLabel/index.js
new file mode 100644
index 00000000000..b0ed11c58bb
--- /dev/null
+++ b/src/components/ExpensiTextInput/ExpensiTextInputLabel/index.js
@@ -0,0 +1,30 @@
+import React, {memo} from 'react';
+import {Animated} from 'react-native';
+import styles from '../../../styles/styles';
+import propTypes from './propTypes';
+
+const ExpensiTextInputLabel = ({
+ label,
+ labelTranslateY,
+ labelTranslateX,
+ labelScale,
+}) => (
+
+ {label}
+
+);
+
+ExpensiTextInputLabel.propTypes = propTypes;
+ExpensiTextInputLabel.displayName = 'ExpensiTextInputLabel';
+
+export default memo(ExpensiTextInputLabel);
diff --git a/src/components/ExpensiTextInput/ExpensiTextInputLabel/index.native.js b/src/components/ExpensiTextInput/ExpensiTextInputLabel/index.native.js
new file mode 100644
index 00000000000..4e8159d5e2a
--- /dev/null
+++ b/src/components/ExpensiTextInput/ExpensiTextInputLabel/index.native.js
@@ -0,0 +1,29 @@
+import React, {memo} from 'react';
+import {Animated} from 'react-native';
+import styles from '../../../styles/styles';
+import propTypes from './propTypes';
+
+const ExpensiTextInputLabel = ({
+ label,
+ labelTranslateX,
+ labelTranslateY,
+ labelScale,
+}) => (
+
+ {label}
+
+);
+
+ExpensiTextInputLabel.propTypes = propTypes;
+ExpensiTextInputLabel.displayName = 'ExpensiTextInputLabel';
+
+export default memo(ExpensiTextInputLabel);
diff --git a/src/components/ExpensiTextInput/ExpensiTextInputLabel/propTypes.js b/src/components/ExpensiTextInput/ExpensiTextInputLabel/propTypes.js
new file mode 100644
index 00000000000..cfaf5f14d37
--- /dev/null
+++ b/src/components/ExpensiTextInput/ExpensiTextInputLabel/propTypes.js
@@ -0,0 +1,18 @@
+import PropTypes from 'prop-types';
+import {Animated} from 'react-native';
+
+const propTypes = {
+ /** Label */
+ label: PropTypes.string,
+
+ /** Label vertical translate */
+ labelTranslateY: PropTypes.instanceOf(Animated.Value).isRequired,
+
+ /** Label horizontal translate */
+ labelTranslateX: PropTypes.instanceOf(Animated.Value).isRequired,
+
+ /** Label scale */
+ labelScale: PropTypes.instanceOf(Animated.Value).isRequired,
+};
+
+export default propTypes;
diff --git a/src/components/ExpensiTextInput/index.android.js b/src/components/ExpensiTextInput/index.android.js
new file mode 100644
index 00000000000..35b9a15cfd8
--- /dev/null
+++ b/src/components/ExpensiTextInput/index.android.js
@@ -0,0 +1,23 @@
+import React, {forwardRef} from 'react';
+import styles from '../../styles/styles';
+import BaseExpensiTextInput from './BaseExpensiTextInput';
+import {propTypes, defaultProps} from './propTypes';
+
+const ExpensiTextInput = forwardRef((props, ref) => (
+
+));
+
+ExpensiTextInput.propTypes = propTypes;
+ExpensiTextInput.defaultProps = defaultProps;
+ExpensiTextInput.displayName = 'ExpensiTextInput';
+
+export default ExpensiTextInput;
diff --git a/src/components/ExpensiTextInput/index.ios.js b/src/components/ExpensiTextInput/index.ios.js
new file mode 100644
index 00000000000..9e84b12d135
--- /dev/null
+++ b/src/components/ExpensiTextInput/index.ios.js
@@ -0,0 +1,19 @@
+import React, {forwardRef} from 'react';
+import styles from '../../styles/styles';
+import BaseExpensiTextInput from './BaseExpensiTextInput';
+import {propTypes, defaultProps} from './propTypes';
+
+const ExpensiTextInput = forwardRef((props, ref) => (
+
+));
+
+ExpensiTextInput.propTypes = propTypes;
+ExpensiTextInput.defaultProps = defaultProps;
+ExpensiTextInput.displayName = 'ExpensiTextInput';
+
+export default ExpensiTextInput;
diff --git a/src/components/ExpensiTextInput/index.js b/src/components/ExpensiTextInput/index.js
new file mode 100644
index 00000000000..c56aaee213e
--- /dev/null
+++ b/src/components/ExpensiTextInput/index.js
@@ -0,0 +1,20 @@
+import React, {forwardRef} from 'react';
+import styles from '../../styles/styles';
+import BaseExpensiTextInput from './BaseExpensiTextInput';
+import {propTypes, defaultProps} from './propTypes';
+
+const ExpensiTextInput = forwardRef((props, ref) => (
+
+));
+
+ExpensiTextInput.propTypes = propTypes;
+ExpensiTextInput.defaultProps = defaultProps;
+ExpensiTextInput.displayName = 'ExpensiTextInput';
+
+export default ExpensiTextInput;
diff --git a/src/components/ExpensiTextInput/propTypes.js b/src/components/ExpensiTextInput/propTypes.js
new file mode 100644
index 00000000000..6231018ccee
--- /dev/null
+++ b/src/components/ExpensiTextInput/propTypes.js
@@ -0,0 +1,39 @@
+import PropTypes from 'prop-types';
+
+const propTypes = {
+ /** Input label */
+ label: PropTypes.string,
+
+ /** Input value */
+ value: PropTypes.string.isRequired,
+
+ /** Input value placeholder */
+ placeholder: PropTypes.string,
+
+ /** Should the input be styled for errors */
+ hasError: PropTypes.bool,
+
+ /** Customize the ExpensiTextInput container */
+ containerStyles: PropTypes.arrayOf(PropTypes.object),
+
+ /** label translate x */
+ translateX: PropTypes.number,
+
+ /** input style */
+ inputStyle: PropTypes.arrayOf(PropTypes.object),
+
+ /** should ignore labels translate x? */
+ ignoreLabelTranslateX: PropTypes.bool,
+};
+
+const defaultProps = {
+ label: '',
+ placeholder: '',
+ error: false,
+ containerStyles: [],
+ translateX: -22,
+ inputStyle: [],
+ ignoreLabelTranslateX: false,
+};
+
+export {propTypes, defaultProps};
diff --git a/src/components/FullNameInputRow.js b/src/components/FullNameInputRow.js
index 180a642c526..6ed98c5be04 100644
--- a/src/components/FullNameInputRow.js
+++ b/src/components/FullNameInputRow.js
@@ -1,11 +1,10 @@
import React from 'react';
import PropTypes from 'prop-types';
-import {TextInput, View} from 'react-native';
+import {View} from 'react-native';
import _ from 'underscore';
import styles from '../styles/styles';
-import Text from './Text';
-import themeColors from '../styles/themes/default';
import withLocalize, {withLocalizePropTypes} from './withLocalize';
+import ExpensiTextInput from './ExpensiTextInput';
const propTypes = {
...withLocalizePropTypes,
@@ -19,15 +18,9 @@ const propTypes = {
/** Used to prefill the firstName input, can also be used to make it a controlled input */
firstName: PropTypes.string,
- /** Placeholder text for the firstName input */
- firstNamePlaceholder: PropTypes.string,
-
/** Used to prefill the lastName input, can also be used to make it a controlled input */
lastName: PropTypes.string,
- /** Placeholder text for the lastName input */
- lastNamePlaceholder: PropTypes.string,
-
/** Additional styles to add after local styles */
style: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.object),
@@ -36,9 +29,7 @@ const propTypes = {
};
const defaultProps = {
firstName: '',
- firstNamePlaceholder: null,
lastName: '',
- lastNamePlaceholder: null,
style: {},
};
@@ -46,35 +37,27 @@ const FullNameInputRow = ({
translate,
onChangeFirstName, onChangeLastName,
firstName, lastName,
- firstNamePlaceholder,
- lastNamePlaceholder,
style,
}) => {
const additionalStyles = _.isArray(style) ? style : [style];
return (
-
- {translate('common.firstName')}
-
-
-
- {translate('common.lastName')}
-
-
diff --git a/src/components/IOUConfirmationList.js b/src/components/IOUConfirmationList.js
index 5d3de1ee96e..33ba326f204 100755
--- a/src/components/IOUConfirmationList.js
+++ b/src/components/IOUConfirmationList.js
@@ -1,7 +1,7 @@
import React, {Component} from 'react';
import {View} from 'react-native';
import PropTypes from 'prop-types';
-import {ScrollView, TextInput} from 'react-native-gesture-handler';
+import {ScrollView} from 'react-native-gesture-handler';
import {withOnyx} from 'react-native-onyx';
import {withSafeAreaInsets} from 'react-native-safe-area-context';
import _ from 'underscore';
@@ -20,6 +20,7 @@ import SafeAreaInsetPropTypes from '../pages/SafeAreaInsetPropTypes';
import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions';
import compose from '../libs/compose';
import FixedFooter from './FixedFooter';
+import ExpensiTextInput from './ExpensiTextInput';
import CONST from '../CONST';
const propTypes = {
@@ -345,12 +346,9 @@ class IOUConfirmationList extends Component {
disableRowInteractivity={!this.props.hasMultipleParticipants}
optionHoveredStyle={hoverStyle}
/>
-
- {this.props.translate('iOUConfirmationList.whatsItFor')}
-
- {
+ onStartShouldSetPanResponder={() => {
const isDoubleClick = new Date().getTime() - this.lastClickTime <= this.doubleClickInterval;
this.lastClickTime = new Date().getTime();
// Let ImageZoom handle the event if the tap is more than one touchPoint or if we are zoomed in
- if (e.nativeEvent.touches.length === 2 || this.imageZoomScale !== 1) {
+ if (this.amountOfTouches === 2 || this.imageZoomScale !== 1) {
return true;
}
@@ -95,6 +118,20 @@ class ImageView extends PureComponent {
this.setState({imageHeight, imageWidth});
}}
/>
+ {/**
+ Create an invisible view on top of the image so we can capture and set the amount of touches before
+ the ImageZoom's PanResponder does. Children will be triggered first, so this needs to be inside the
+ ImageZoom to work
+ */}
+
);
diff --git a/src/components/LocalePicker.js b/src/components/LocalePicker.js
index b64707cc16a..6df228ca477 100644
--- a/src/components/LocalePicker.js
+++ b/src/components/LocalePicker.js
@@ -1,9 +1,6 @@
import React from 'react';
import {withOnyx} from 'react-native-onyx';
import PropTypes from 'prop-types';
-import styles from '../styles/styles';
-import Picker from './Picker';
-import Text from './Text';
import compose from '../libs/compose';
import {setLocale} from '../libs/actions/App';
import withLocalize, {withLocalizePropTypes} from './withLocalize';
@@ -11,6 +8,7 @@ import ONYXKEYS from '../ONYXKEYS';
import CONST from '../CONST';
import Permissions from '../libs/Permissions';
import {translate} from '../libs/translate';
+import ExpensiPicker from './ExpensiPicker';
const propTypes = {
/** Indicates which locale the user currently has selected */
@@ -51,23 +49,17 @@ const LocalePicker = ({
}
return (
- <>
- {size === 'normal' && (
-
- {translate('preferencesPage.language')}
-
- )}
- {
- if (locale !== preferredLocale) {
- setLocale(locale);
- }
- }}
- items={Object.values(localesToLanguages)}
- size={size}
- value={preferredLocale}
- />
- >
+ {
+ if (locale !== preferredLocale) {
+ setLocale(locale);
+ }
+ }}
+ items={Object.values(localesToLanguages)}
+ size={size}
+ value={preferredLocale}
+ />
);
};
diff --git a/src/components/MenuItem.js b/src/components/MenuItem.js
index f046068643e..77440b9e345 100644
--- a/src/components/MenuItem.js
+++ b/src/components/MenuItem.js
@@ -140,7 +140,7 @@ const MenuItem = ({
{title}
{description && (
-
+
{description}
)}
@@ -151,7 +151,7 @@ const MenuItem = ({
{subtitle && (
{subtitle}
diff --git a/src/components/OptionsSelector.js b/src/components/OptionsSelector.js
index beb39197ac8..fe246d6b4f3 100755
--- a/src/components/OptionsSelector.js
+++ b/src/components/OptionsSelector.js
@@ -2,12 +2,11 @@ import _ from 'underscore';
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {View} from 'react-native';
-import TextInputWithFocusStyles from './TextInputWithFocusStyles';
import OptionsList from './OptionsList';
import styles from '../styles/styles';
-import themeColors from '../styles/themes/default';
import optionPropTypes from './optionPropTypes';
import withLocalize, {withLocalizePropTypes} from './withLocalize';
+import ExpensiTextInput from './ExpensiTextInput';
const propTypes = {
/** Callback to fire when a row is tapped */
@@ -195,16 +194,13 @@ class OptionsSelector extends Component {
return (
- this.textInput = el}
- style={[styles.textInput]}
value={this.props.value}
onChangeText={this.props.onChangeText}
onKeyPress={this.handleKeyPress}
placeholder={this.props.placeholderText
|| this.props.translate('optionsSelector.nameEmailOrPhoneNumber')}
- placeholderTextColor={themeColors.placeholderText}
/>
),
+ size: 'normal',
};
export {
diff --git a/src/components/Picker/index.js b/src/components/Picker/index.js
index b963c81601b..28ff3ff877e 100644
--- a/src/components/Picker/index.js
+++ b/src/components/Picker/index.js
@@ -2,40 +2,35 @@ import React from 'react';
import RNPickerSelect from 'react-native-picker-select';
import styles from '../../styles/styles';
-import pickerDisabledStyles from './pickerDisabledStyles';
import * as pickerPropTypes from './PickerPropTypes';
+import pickerStyles from './pickerStyles';
const Picker = ({
onChange,
items,
- useDisabledStyles,
placeholder,
value,
icon,
disabled,
+ onOpen,
+ onClose,
size,
-}) => {
- let pickerStyles;
- if (size === 'small') {
- pickerStyles = styles.pickerSmall;
- } else {
- pickerStyles = useDisabledStyles ? pickerDisabledStyles : styles.picker;
- }
+}) => (
+ icon(size)}
+ disabled={disabled}
+ fixAndroidTouchableBug
+ onOpen={onOpen}
+ onClose={onClose}
+ />
+);
- return (
- icon(size)}
- disabled={disabled}
- fixAndroidTouchableBug
- />
- );
-};
Picker.propTypes = pickerPropTypes.propTypes;
Picker.defaultProps = pickerPropTypes.defaultProps;
diff --git a/src/components/Picker/pickerDisabledStyles/index.android.js b/src/components/Picker/pickerDisabledStyles/index.android.js
deleted file mode 100644
index d978155e75d..00000000000
--- a/src/components/Picker/pickerDisabledStyles/index.android.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import styles from '../../../styles/styles';
-
-export default {
- ...styles.picker,
- inputAndroid: [styles.picker.inputAndroid, styles.textInput, styles.disabledTextInput],
-};
diff --git a/src/components/Picker/pickerDisabledStyles/index.ios.js b/src/components/Picker/pickerDisabledStyles/index.ios.js
deleted file mode 100644
index 83a8b81de73..00000000000
--- a/src/components/Picker/pickerDisabledStyles/index.ios.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import styles from '../../../styles/styles';
-
-export default {
- ...styles.picker,
- inputIOS: [styles.picker.inputIOS, styles.textInput, styles.disabledTextInput],
-};
diff --git a/src/components/Picker/pickerDisabledStyles/index.js b/src/components/Picker/pickerDisabledStyles/index.js
deleted file mode 100644
index 6fc2e9f3bbd..00000000000
--- a/src/components/Picker/pickerDisabledStyles/index.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import styles from '../../../styles/styles';
-
-export default {
- ...styles.picker,
- inputWeb: [styles.picker.inputWeb, styles.textInput, styles.disabledTextInput],
-};
diff --git a/src/components/Picker/pickerStyles/index.android.js b/src/components/Picker/pickerStyles/index.android.js
new file mode 100644
index 00000000000..ea9a2882a09
--- /dev/null
+++ b/src/components/Picker/pickerStyles/index.android.js
@@ -0,0 +1,8 @@
+import styles from '../../../styles/styles';
+
+const pickerStyles = disabled => ({
+ ...styles.expensiPicker(disabled),
+ inputAndroid: styles.expensiPicker(disabled).inputNative,
+});
+
+export default pickerStyles;
diff --git a/src/components/Picker/pickerStyles/index.ios.js b/src/components/Picker/pickerStyles/index.ios.js
new file mode 100644
index 00000000000..0c144fd8ad2
--- /dev/null
+++ b/src/components/Picker/pickerStyles/index.ios.js
@@ -0,0 +1,8 @@
+import styles from '../../../styles/styles';
+
+const pickerStyles = disabled => ({
+ ...styles.expensiPicker(disabled),
+ inputIOS: styles.expensiPicker(disabled).inputNative,
+});
+
+export default pickerStyles;
diff --git a/src/components/Picker/pickerStyles/index.js b/src/components/Picker/pickerStyles/index.js
new file mode 100644
index 00000000000..7c11a9f8ed8
--- /dev/null
+++ b/src/components/Picker/pickerStyles/index.js
@@ -0,0 +1,5 @@
+import styles from '../../../styles/styles';
+
+const pickerStyles = disabled => styles.expensiPicker(disabled);
+
+export default pickerStyles;
diff --git a/src/components/TextInputWithLabel.js b/src/components/TextInputWithLabel.js
index 63cdd87dbb8..40ac02a9fcb 100644
--- a/src/components/TextInputWithLabel.js
+++ b/src/components/TextInputWithLabel.js
@@ -54,7 +54,7 @@ const TextInputWithLabel = props => (
)}
diff --git a/src/components/VBALoadingIndicator.js b/src/components/VBALoadingIndicator.js
new file mode 100644
index 00000000000..a53c83e38e3
--- /dev/null
+++ b/src/components/VBALoadingIndicator.js
@@ -0,0 +1,35 @@
+import React from 'react';
+import {Image, StyleSheet, View} from 'react-native';
+import styles from '../styles/styles';
+import CONST from '../CONST';
+import withLocalize, {withLocalizePropTypes} from './withLocalize';
+import Text from './Text';
+
+const propTypes = {
+ ...withLocalizePropTypes,
+};
+
+const VBALoadingIndicator = ({translate}) => (
+
+
+
+
+
+ {translate('vbaLoadingAnimation.oneMoment')}
+
+
+ {translate('vbaLoadingAnimation.explanationLine')}
+
+
+
+
+);
+
+VBALoadingIndicator.propTypes = propTypes;
+
+export default withLocalize(VBALoadingIndicator);
diff --git a/src/languages/en.js b/src/languages/en.js
index a2d9379d2a9..39fafd4d7da 100755
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -14,6 +14,7 @@ export default {
add: 'Add',
resend: 'Resend',
save: 'Save',
+ saveChanges: 'Save Changes',
password: 'Password',
profile: 'Profile',
payments: 'Payments',
@@ -368,7 +369,7 @@ export default {
addressState: 'Please select a valid state',
incorporationDate: 'Please enter a valid incorporation date',
incorporationState: 'Please enter a valid Incorporation State',
- industryCode: 'Please enter a valid industry classification code',
+ industryCode: 'Please enter a valid industry classification code. Must be 6 digits.',
restrictedBusiness: 'Please confirm company is not on the list of restricted businesses',
routingNumber: 'Please enter a valid Routing Number',
companyType: 'Please enter a valid Company Type',
@@ -546,6 +547,10 @@ export default {
certify: 'Must certify information is true and accurate',
},
},
+ vbaLoadingAnimation: {
+ oneMoment: 'One moment...',
+ explanationLine: 'We’re taking a look at your information. You will be able to continue with next steps shortly.',
+ },
session: {
offlineMessageRetry: 'Looks like you\'re offline. Please check your connection and try again.',
offlineMessage: 'Looks like you\'re offline.',
@@ -563,6 +568,7 @@ export default {
helpText: 'Name your Workspace before enabling your Expensify Cards!',
getStarted: 'Get started!',
genericFailureMessage: 'An error occurred creating the workspace, please try again.',
+ successMessage: 'Workspace created',
},
people: {
assignee: 'Assignee',
@@ -597,6 +603,9 @@ export default {
genericFailureMessage: 'An error occurred updating the workspace, please try again.',
avatarUploadFailureMessage: 'An error occurred uploading the avatar, please try again.',
},
+ error: {
+ growlMessageInvalidPolicy: 'Invalid workspace! You can create a new workspace!',
+ },
},
requestCallPage: {
requestACall: 'Request a Call',
diff --git a/src/languages/es.js b/src/languages/es.js
index c93e0a4103e..c04745f3651 100644
--- a/src/languages/es.js
+++ b/src/languages/es.js
@@ -14,6 +14,7 @@ export default {
add: 'Agregar',
resend: 'Reenviar',
save: 'Guardar',
+ saveChanges: 'Guardar cambios',
password: 'Contraseña',
profile: 'Perfil',
payments: 'Pagos',
@@ -548,6 +549,10 @@ export default {
certify: 'Debe certificar que la información es verdadera y precisa',
},
},
+ vbaLoadingAnimation: {
+ oneMoment: 'Un momento...',
+ explanationLine: 'Estamos verificando tu información y podrás continuar con los siguientes pasos en unos momentos.',
+ },
session: {
offlineMessageRetry: 'Parece que estás desconectado. Por favor chequea tu conexión e inténtalo otra vez',
offlineMessage: 'Parece que estás desconectado.',
@@ -565,6 +570,7 @@ export default {
helpText: 'Elige un nombre para el espacio de trabajo antes de activar las tarjetas Expensify',
getStarted: '¡Empezar!',
genericFailureMessage: 'Se ha producido un error al intentar crear el Workspace. Por favor, inténtalo de nuevo.',
+ successMessage: 'Espacio de trabajo creado',
},
people: {
assignee: 'Persona asignada',
@@ -599,6 +605,9 @@ export default {
genericFailureMessage: 'Se produjo un error al guardar el espacio de trabajo. Por favor, inténtalo de nuevo.',
avatarUploadFailureMessage: 'No se pudo subir el avatar. Por favor, inténtalo de nuevo.',
},
+ error: {
+ growlMessageInvalidPolicy: '¡Espacio de trabajo no válido! ¡Puedes crear un nuevo espacio de trabajo!',
+ },
},
requestCallPage: {
requestACall: 'Llámame por teléfono',
diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js
index f4f1379628d..45f6077b922 100644
--- a/src/libs/Navigation/AppNavigator/AuthScreens.js
+++ b/src/libs/Navigation/AppNavigator/AuthScreens.js
@@ -65,6 +65,8 @@ import Timers from '../../Timers';
import ValidateLoginNewWorkspacePage from '../../../pages/ValidateLoginNewWorkspacePage';
import ValidateLogin2FANewWorkspacePage from '../../../pages/ValidateLogin2FANewWorkspacePage';
import WorkspaceSettingsDrawerNavigator from './WorkspaceSettingsDrawerNavigator';
+import spacing from '../../../styles/utilities/spacing';
+import CardOverlay from '../../../components/CardOverlay';
import defaultScreenOptions from './defaultScreenOptions';
Onyx.connect({
@@ -253,10 +255,14 @@ class AuthScreens extends React.Component {
};
const fullscreenModalScreenOptions = {
...commonModalScreenOptions,
- cardStyle: {...styles.fullscreenCard},
+ cardStyle: {
+ ...styles.fullscreenCard,
+ padding: this.props.isSmallScreenWidth ? spacing.p0.padding : spacing.p5.padding,
+ },
cardStyleInterpolator: props => modalCardStyleInterpolator(this.props.isSmallScreenWidth, true, props),
- cardOverlayEnabled: false,
+ cardOverlayEnabled: !this.props.isSmallScreenWidth,
isFullScreenModal: true,
+ cardOverlay: CardOverlay,
};
return (
@@ -279,6 +285,7 @@ class AuthScreens extends React.Component {
// prevent unnecessary scrolling
cardStyle: {
overflow: 'hidden',
+ height: '100%',
},
}}
component={MainDrawerNavigator}
diff --git a/src/libs/Navigation/AppNavigator/BaseDrawerNavigator.js b/src/libs/Navigation/AppNavigator/BaseDrawerNavigator.js
index e794db66960..5a6d277b195 100644
--- a/src/libs/Navigation/AppNavigator/BaseDrawerNavigator.js
+++ b/src/libs/Navigation/AppNavigator/BaseDrawerNavigator.js
@@ -2,6 +2,7 @@ import React from 'react';
import _ from 'underscore';
import PropTypes from 'prop-types';
import {createDrawerNavigator} from '@react-navigation/drawer';
+import {View} from 'react-native';
import styles, {getNavigationDrawerStyle, getNavigationDrawerType} from '../../../styles/styles';
import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions';
@@ -22,38 +23,51 @@ const propTypes = {
/** Drawer content Component */
drawerContent: PropTypes.elementType.isRequired,
+ /** If it's the main screen, don't wrap the content even if it's a full screen modal. */
+ isMainScreen: PropTypes.bool,
+
/** Window Dimensions props */
...windowDimensionsPropTypes,
};
const Drawer = createDrawerNavigator();
-const BaseDrawerNavigator = props => (
-
- {_.map(props.screens, screen => (
-
- ))}
-
-);
+const BaseDrawerNavigator = (props) => {
+ const content = (
+
+ {_.map(props.screens, screen => (
+
+ ))}
+
+ );
+
+ if (!props.isMainScreen && !props.isSmallScreenWidth) {
+ return (
+
+ {content}
+
+ );
+ }
+
+ return content;
+};
BaseDrawerNavigator.propTypes = propTypes;
BaseDrawerNavigator.displayName = 'BaseDrawerNavigator';
diff --git a/src/libs/Navigation/AppNavigator/MainDrawerNavigator.js b/src/libs/Navigation/AppNavigator/MainDrawerNavigator.js
index de10b3fc936..382aad6fef8 100644
--- a/src/libs/Navigation/AppNavigator/MainDrawerNavigator.js
+++ b/src/libs/Navigation/AppNavigator/MainDrawerNavigator.js
@@ -54,6 +54,7 @@ const MainDrawerNavigator = (props) => {
initialParams,
},
]}
+ isMainScreen
/>
);
};
diff --git a/src/libs/actions/BankAccounts.js b/src/libs/actions/BankAccounts.js
index b3f016ac3ab..bd5ebe15bf8 100644
--- a/src/libs/actions/BankAccounts.js
+++ b/src/libs/actions/BankAccounts.js
@@ -10,6 +10,7 @@ import BankAccount from '../models/BankAccount';
import promiseAllSettled from '../promiseAllSettled';
import Growl from '../Growl';
import {translateLocal} from '../translate';
+import Navigation from '../Navigation/Navigation';
/**
* List of bank accounts. This data should not be stored in Onyx since it contains unmasked PANs.
@@ -328,6 +329,11 @@ function fetchUserWallet() {
* @param {String} [stepToOpen]
*/
function fetchFreePlanVerifiedBankAccount(stepToOpen) {
+ const oldACHData = {
+ accountNumber: reimbursementAccountInSetup.accountNumber || '',
+ routingNumber: reimbursementAccountInSetup.routingNumber || '',
+ };
+
// We are using set here since we will rely on data from the server (not local data) to populate the VBA flow
// and determine which step to navigate to.
Onyx.set(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {loading: true});
@@ -478,7 +484,18 @@ function fetchFreePlanVerifiedBankAccount(stepToOpen) {
goToWithdrawalAccountSetupStep(currentStep, achData);
})
.finally(() => {
- Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {loading: false});
+ const dataToMerge = {
+ loading: false,
+ };
+
+ // If we didn't get a routingNumber and accountNumber from the response and we have previously saved
+ // values, autofill them
+ if (!reimbursementAccountInSetup.routingNumber && !reimbursementAccountInSetup.accountNumber
+ && oldACHData.routingNumber && oldACHData.accountNumber) {
+ dataToMerge.achData = oldACHData;
+ }
+
+ Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, dataToMerge);
});
});
}
@@ -553,8 +570,20 @@ function validateBankAccount(bankAccountID, validateCode) {
Growl.show('Bank Account successfully validated!', CONST.GROWL.SUCCESS, 3000);
API.User_IsUsingExpensifyCard()
.then(({isUsingExpensifyCard}) => {
+ const reimbursementAccount = {
+ loading: false,
+ error: '',
+ achData: {state: BankAccount.STATE.OPEN},
+ };
+
+ if (isUsingExpensifyCard) {
+ Navigation.dismissModal();
+ } else {
+ reimbursementAccount.achData.currentStep = CONST.BANK_ACCOUNT.STEP.ENABLE;
+ }
+
Onyx.merge(ONYXKEYS.USER, {isUsingExpensifyCard});
- Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {loading: false, error: ''});
+ Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, reimbursementAccount);
});
return;
}
@@ -602,9 +631,18 @@ function setupWithdrawalAccount(data) {
}
API.BankAccount_SetupWithdrawal(newACHData)
+ /* eslint-disable arrow-body-style */
+ .then((response) => {
+ // Without this block, we can call merge again with the achData before this merge finishes, resulting in
+ // the original achData overwriting the data we're trying to set here. With this block, we ensure that the
+ // newACHData is set in Onyx before we call merge on the reimbursementAccount key again.
+ return Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {
+ loading: false,
+ achData: {...newACHData},
+ })
+ .then(() => Promise.resolve(response));
+ })
.then((response) => {
- Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {loading: false, achData: {...newACHData}});
-
const currentStep = newACHData.currentStep;
let achData = lodashGet(response, 'achData', {});
let error = lodashGet(achData, CONST.BANK_ACCOUNT.VERIFICATIONS.ERROR_MESSAGE);
diff --git a/src/libs/actions/Policy.js b/src/libs/actions/Policy.js
index fd3f3ecc063..2f5bf480e14 100644
--- a/src/libs/actions/Policy.js
+++ b/src/libs/actions/Policy.js
@@ -87,7 +87,14 @@ function getPolicyList() {
avatarURL: lodashGet(policy, 'value.avatarURL', ''),
},
}), {});
- Onyx.mergeCollection(ONYXKEYS.COLLECTION.POLICY, policyDataToStore);
+
+ Onyx.mergeCollection(ONYXKEYS.COLLECTION.POLICY, {
+ // Erase all policies in Onyx
+ ...(_.reduce(_.keys(allPolicies), (memo, key) => ({...memo, [key]: null}), {})),
+
+ // And overwrite them with only the ones returned by the API call
+ ...policyDataToStore,
+ });
}
});
}
@@ -185,6 +192,7 @@ function invite(logins, welcomeNote, policyID) {
* @param {String} [name]
*/
function create(name = '') {
+ let res = null;
API.Policy_Create({type: CONST.POLICY.TYPE.FREE, policyName: name})
.then((response) => {
if (response.jsonCode !== 200) {
@@ -193,16 +201,18 @@ function create(name = '') {
Growl.error(errorMessage, 5000);
return;
}
+ res = response;
- Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${response.policyID}`, {
+ return Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${response.policyID}`, {
employeeList: getSimplifiedEmployeeList(response.policy.employeeList),
id: response.policyID,
type: response.policy.type,
name: response.policy.name,
role: CONST.POLICY.ROLE.ADMIN,
});
+ }).then(() => {
Navigation.dismissModal();
- Navigation.navigate(ROUTES.getWorkspaceCardRoute(response.policyID));
+ Navigation.navigate(ROUTES.getWorkspaceCardRoute(res.policyID));
Growl.success(translateLocal('workspace.new.successMessage'));
});
}
diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js
index 9976e32ca4a..4f9a766e8f3 100644
--- a/src/libs/actions/Report.js
+++ b/src/libs/actions/Report.js
@@ -855,6 +855,7 @@ function fetchOrCreateChatReport(participants, shouldNavigate = true) {
.then((data) => {
if (data.jsonCode !== 200) {
console.error(data.message);
+ Growl.error(data.message);
return;
}
diff --git a/src/pages/EnablePayments/AdditionalDetailsStep.js b/src/pages/EnablePayments/AdditionalDetailsStep.js
index e647bb1f3d8..dd65d99b9a4 100644
--- a/src/pages/EnablePayments/AdditionalDetailsStep.js
+++ b/src/pages/EnablePayments/AdditionalDetailsStep.js
@@ -12,12 +12,12 @@ import Navigation from '../../libs/Navigation/Navigation';
import styles from '../../styles/styles';
import Button from '../../components/Button';
import Text from '../../components/Text';
-import TextInputWithLabel from '../../components/TextInputWithLabel';
import {activateWallet} from '../../libs/actions/BankAccounts';
import CONST from '../../CONST';
import compose from '../../libs/compose';
import ONYXKEYS from '../../ONYXKEYS';
import TextLink from '../../components/TextLink';
+import ExpensiTextInput from '../../components/ExpensiTextInput';
const propTypes = {
...withLocalizePropTypes,
@@ -128,7 +128,7 @@ class AdditionalDetailsStep extends React.Component {
{_.map(this.fields, field => (
- this.setState({[field.fieldName]: val})}
diff --git a/src/pages/EnablePayments/TermsPage/LongTermsForm.js b/src/pages/EnablePayments/TermsPage/LongTermsForm.js
index af7e7b1363b..ae22740a3de 100644
--- a/src/pages/EnablePayments/TermsPage/LongTermsForm.js
+++ b/src/pages/EnablePayments/TermsPage/LongTermsForm.js
@@ -85,7 +85,7 @@ const getLongTermsSections = () => termsData.map((section, index) => (
}
-
+
{section.details}
diff --git a/src/pages/ReimbursementAccount/BankAccountStep.js b/src/pages/ReimbursementAccount/BankAccountStep.js
index b38563acdef..bcedfdb74cd 100644
--- a/src/pages/ReimbursementAccount/BankAccountStep.js
+++ b/src/pages/ReimbursementAccount/BankAccountStep.js
@@ -15,12 +15,12 @@ import Icon from '../../components/Icon';
import colors from '../../styles/colors';
import Navigation from '../../libs/Navigation/Navigation';
import CONST from '../../CONST';
-import TextInputWithLabel from '../../components/TextInputWithLabel';
import AddPlaidBankAccount from '../../components/AddPlaidBankAccount';
import CheckboxWithLabel from '../../components/CheckboxWithLabel';
import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize';
import exampleCheckImage from '../../../assets/images/example-check-image.png';
import Text from '../../components/Text';
+import ExpensiTextInput from '../../components/ExpensiTextInput';
import {
goToWithdrawalAccountSetupStep,
hideExistingOwnersError,
@@ -207,14 +207,14 @@ class BankAccountStep extends React.Component {
style={[styles.exampleCheckImage, styles.mb5]}
source={exampleCheckImage}
/>
- this.setState({routingNumber})}
disabled={shouldDisableInputs}
/>
- acc || !this.state[curr].trim(), false);
+ const missingRequiredFields = this.requiredFields.reduce((acc, curr) => acc || !this.state[curr].trim(), false);
+ const shouldDisableSubmitButton = !this.state.hasNoConnectionToCannabis || missingRequiredFields;
+
return (
<>
-
- {this.props.translate('companyStep.subtitle')}
-
- {this.props.translate('companyStep.subtitle')}
+ this.setState({companyName})}
value={this.state.companyName}
disabled={shouldDisableCompanyName}
/>
- this.setState({addressStreet})}
@@ -155,7 +148,7 @@ class CompanyStep extends React.Component {
/>
- this.setState({addressCity})}
value={this.state.addressCity}
@@ -169,13 +162,13 @@ class CompanyStep extends React.Component {
/>
- this.setState({addressZipCode})}
value={this.state.addressZipCode}
/>
-
- this.setState({website})}
value={this.state.website}
/>
-
-
- {this.props.translate('companyStep.companyType')}
-
- ({value, label}))}
- onChange={incorporationType => this.setState({incorporationType})}
- value={this.state.incorporationType}
- placeholder={{value: '', label: 'Type'}}
- />
+
+ ({value, label}))}
+ onChange={incorporationType => this.setState({incorporationType})}
+ value={this.state.incorporationType}
+ placeholder={{value: '', label: 'Type'}}
+ />
+
{/* TODO: Replace with date picker */}
- this.setState({incorporationDate})}
value={this.state.incorporationDate}
@@ -225,7 +218,7 @@ class CompanyStep extends React.Component {
{/* TODO: Replace with NAICS picker */}
- this.setState({industryCode})}
value={this.state.industryCode}
/>
- this.setState({password})}
value={this.state.password}
@@ -263,6 +256,15 @@ class CompanyStep extends React.Component {
/>
+ this.setState({isConfirmModalOpen: false})}
+ prompt="Please double check any highlighted fields and try again."
+ isVisible={this.state.isConfirmModalOpen}
+ confirmText="Got it"
+ shouldShowCancelButton={false}
+ />
+