diff --git a/android/app/build.gradle b/android/app/build.gradle
index 363d01f59456..9febb141ca00 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -152,8 +152,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1001018508
- versionName "1.1.85-8"
+ versionCode 1001018604
+ versionName "1.1.86-4"
}
splits {
abi {
diff --git a/contributingGuides/CONTRIBUTING.md b/contributingGuides/CONTRIBUTING.md
index 5a06599d8936..2cde9aff521f 100644
--- a/contributingGuides/CONTRIBUTING.md
+++ b/contributingGuides/CONTRIBUTING.md
@@ -37,6 +37,8 @@ Payment for your contributions will be made no less than 7 days after the pull r
New contributors are limited to working on one job at a time, however experienced contributors may work on numerous jobs simultaneously.
+Please be aware that compensation for any support in solving an issue is provided **entirely at Expensify’s discretion**. Personal time or resources applied towards investigating a proposal **will not guarantee compensation**. Compensation is only guaranteed to those who **[propose a solution and get hired for that job](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#propose-a-solution-for-the-job)**. We understand there may be cases where a selected proposal may take inspiration from a previous proposal. Unfortunately, it’s not possible for us to evaluate every individual case and we have no process that can efficiently do so. Issues with higher rewards come with higher risk factors so try to keep things civil and make the best proposal you can. Once again, **any information provided may not necessarily lead to you getting hired for that issue or compensated in any way.**
+
## Finding Jobs
There are two ways you can find a job that you can contribute to:
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index 9bbbbf689168..cb5d5aa80d9e 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -17,7 +17,7 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 1.1.85
+ 1.1.86
CFBundleSignature
????
CFBundleURLTypes
@@ -30,7 +30,7 @@
CFBundleVersion
- 1.1.85.8
+ 1.1.86.4
ITSAppUsesNonExemptEncryption
LSApplicationQueriesSchemes
diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist
index d75ea9a05b32..eb01d7bf5b73 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -15,10 +15,10 @@
CFBundlePackageType
BNDL
CFBundleShortVersionString
- 1.1.85
+ 1.1.86
CFBundleSignature
????
CFBundleVersion
- 1.1.85.8
+ 1.1.86.4
diff --git a/package-lock.json b/package-lock.json
index 8d82e536def4..eb3249d9fce0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "1.1.85-8",
+ "version": "1.1.86-4",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/package.json b/package.json
index 969eb7a54a27..66304d3c7f8f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "1.1.85-8",
+ "version": "1.1.86-4",
"author": "Expensify, Inc.",
"homepage": "https://new.expensify.com",
"description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
diff --git a/src/components/AddPlaidBankAccount.js b/src/components/AddPlaidBankAccount.js
index 62598e658f0f..c0f926430b0c 100644
--- a/src/components/AddPlaidBankAccount.js
+++ b/src/components/AddPlaidBankAccount.js
@@ -148,12 +148,12 @@ class AddPlaidBankAccount extends React.Component {
)}
- {this.props.plaidData.error && (
+ {Boolean(this.props.plaidData.error) && (
{this.props.plaidData.error}
)}
- {token && (
+ {Boolean(token) && (
{
@@ -217,6 +217,7 @@ export default compose(
},
plaidLinkToken: {
key: ONYXKEYS.PLAID_LINK_TOKEN,
+ initWithStoredValues: false,
},
}),
)(AddPlaidBankAccount);
diff --git a/src/components/Avatar.js b/src/components/Avatar.js
index ff51d2c427b8..eb5f28f64efe 100644
--- a/src/components/Avatar.js
+++ b/src/components/Avatar.js
@@ -8,6 +8,7 @@ import themeColors from '../styles/themes/default';
import CONST from '../CONST';
import * as StyleUtils from '../styles/StyleUtils';
import * as Expensicons from './Icon/Expensicons';
+import getAvatarDefaultSource from '../libs/getAvatarDefaultSource';
const propTypes = {
/** Source for the avatar. Can be a URL or an icon. */
@@ -70,7 +71,12 @@ class Avatar extends PureComponent {
/>
)
: (
- this.setState({imageError: true})} />
+ this.setState({imageError: true})}
+ />
)}
);
diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.js b/src/components/EmojiPicker/EmojiPickerMenu/index.js
index 387f58bc6c6b..2c9ef4a3ad33 100755
--- a/src/components/EmojiPicker/EmojiPickerMenu/index.js
+++ b/src/components/EmojiPicker/EmojiPickerMenu/index.js
@@ -86,8 +86,10 @@ class EmojiPickerMenu extends Component {
this.isMobileLandscape = this.isMobileLandscape.bind(this);
this.onSelectionChange = this.onSelectionChange.bind(this);
this.updatePreferredSkinTone = this.updatePreferredSkinTone.bind(this);
+ this.setFirstNonHeaderIndex = this.setFirstNonHeaderIndex.bind(this);
this.currentScrollOffset = 0;
+ this.firstNonHeaderIndex = 0;
this.state = {
filteredEmojis: this.emojis,
@@ -111,6 +113,7 @@ class EmojiPickerMenu extends Component {
this.props.forwardedRef(this.searchInput);
}
this.setupEventHandlers();
+ this.setFirstNonHeaderIndex(this.emojis);
}
componentWillUnmount() {
@@ -126,6 +129,14 @@ class EmojiPickerMenu extends Component {
this.setState({selection: event.nativeEvent.selection});
}
+ /**
+ * Find and store index of the first emoji item
+ * @param {Array} filteredEmojis
+ */
+ setFirstNonHeaderIndex(filteredEmojis) {
+ this.firstNonHeaderIndex = _.findIndex(filteredEmojis, item => !item.spacer && !item.header);
+ }
+
/**
* Setup and attach keypress/mouse handlers for highlight navigation.
*/
@@ -214,10 +225,12 @@ class EmojiPickerMenu extends Component {
* @param {String} arrowKey
*/
highlightAdjacentEmoji(arrowKey) {
- const firstNonHeaderIndex = this.state.filteredEmojis.length === this.emojis.length ? this.numColumns : 0;
+ if (this.state.filteredEmojis.length === 0) {
+ return;
+ }
// Arrow Down and Arrow Right enable arrow navigation when search is focused
- if (this.searchInput && this.searchInput.isFocused() && this.state.filteredEmojis.length) {
+ if (this.searchInput && this.searchInput.isFocused()) {
if (arrowKey !== 'ArrowDown' && arrowKey !== 'ArrowRight') {
return;
}
@@ -242,7 +255,7 @@ class EmojiPickerMenu extends Component {
// If nothing is highlighted and an arrow key is pressed
// select the first emoji
if (this.state.highlightedIndex === -1) {
- this.setState({highlightedIndex: firstNonHeaderIndex});
+ this.setState({highlightedIndex: this.firstNonHeaderIndex});
this.scrollToHighlightedIndex();
return;
}
@@ -273,7 +286,7 @@ class EmojiPickerMenu extends Component {
break;
case 'ArrowLeft':
move(-1,
- () => this.state.highlightedIndex - 1 < firstNonHeaderIndex,
+ () => this.state.highlightedIndex - 1 < this.firstNonHeaderIndex,
() => {
// Reaching start of the list, arrow left set the focus to searchInput.
this.focusInputWithTextSelect();
@@ -286,7 +299,7 @@ class EmojiPickerMenu extends Component {
case 'ArrowUp':
move(
-this.numColumns,
- () => this.state.highlightedIndex - this.numColumns < firstNonHeaderIndex,
+ () => this.state.highlightedIndex - this.numColumns < this.firstNonHeaderIndex,
() => {
// Reaching start of the list, arrow up set the focus to searchInput.
this.focusInputWithTextSelect();
@@ -356,6 +369,7 @@ class EmojiPickerMenu extends Component {
headerIndices: this.unfilteredHeaderIndices,
highlightedIndex: -1,
});
+ this.setFirstNonHeaderIndex(this.emojis);
return;
}
@@ -370,6 +384,7 @@ class EmojiPickerMenu extends Component {
// Remove sticky header indices. There are no headers while searching and we don't want to make emojis sticky
this.setState({filteredEmojis: newFilteredEmojiList, headerIndices: [], highlightedIndex: 0});
+ this.setFirstNonHeaderIndex(newFilteredEmojiList);
}
/**
diff --git a/src/components/ScreenWrapper.js b/src/components/ScreenWrapper.js
index 9167c7cc2dd7..bc6a416dbd01 100644
--- a/src/components/ScreenWrapper.js
+++ b/src/components/ScreenWrapper.js
@@ -1,7 +1,7 @@
import _ from 'underscore';
import React from 'react';
import PropTypes from 'prop-types';
-import {View, KeyboardAvoidingView} from 'react-native';
+import {View} from 'react-native';
import {SafeAreaInsetsContext} from 'react-native-safe-area-context';
import {withOnyx} from 'react-native-onyx';
import styles from '../styles/styles';
@@ -15,10 +15,6 @@ import compose from '../libs/compose';
import ONYXKEYS from '../ONYXKEYS';
import CONST from '../CONST';
import withNavigation from './withNavigation';
-import withWindowDimensions from './withWindowDimensions';
-import OfflineIndicator from './OfflineIndicator';
-import {withNetwork} from './OnyxProvider';
-import networkPropTypes from './networkPropTypes';
const propTypes = {
/** Array of additional styles to add */
@@ -39,13 +35,6 @@ const propTypes = {
// Called when navigated Screen's transition is finished.
onTransitionEnd: PropTypes.func,
- /** Is the window width narrow, like on a mobile device */
- isSmallScreenWidth: PropTypes.bool.isRequired,
-
- /** The behavior to pass to the KeyboardAvoidingView, requires some trial and error depending on the layout/devices used.
- * Search 'switch(behavior)' in ./node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js for more context */
- keyboardAvoidingViewBehavior: PropTypes.oneOf(['padding', 'height', 'position']),
-
// react-navigation navigation object available to screen components
navigation: PropTypes.shape({
// Method to attach listener to Navigation state.
@@ -58,9 +47,6 @@ const propTypes = {
willAlertModalBecomeVisible: PropTypes.bool,
}),
- /** Information about the network */
- network: networkPropTypes.isRequired,
-
};
const defaultProps = {
@@ -72,7 +58,6 @@ const defaultProps = {
addListener: () => {},
},
modal: {},
- keyboardAvoidingViewBehavior: 'padding',
};
class ScreenWrapper extends React.Component {
@@ -120,36 +105,27 @@ class ScreenWrapper extends React.Component {
paddingStyle.paddingTop = paddingTop;
}
- // We always need the safe area padding bottom if we're showing the offline indicator since it is bottom-docked.
- if (this.props.includePaddingBottom || this.props.network.isOffline) {
+ if (this.props.includePaddingBottom) {
paddingStyle.paddingBottom = paddingBottom;
}
return (
-
-
-
- {// If props.children is a function, call it to provide the insets to the children.
- _.isFunction(this.props.children)
- ? this.props.children({
- insets,
- didScreenTransitionEnd: this.state.didScreenTransitionEnd,
- })
- : this.props.children
- }
-
- {this.props.isSmallScreenWidth && this.props.network.isOffline && (
-
-
-
- )}
-
+
+ {// If props.children is a function, call it to provide the insets to the children.
+ _.isFunction(this.props.children)
+ ? this.props.children({
+ insets,
+ didScreenTransitionEnd: this.state.didScreenTransitionEnd,
+ })
+ : this.props.children
+ }
+
);
}}
@@ -163,11 +139,9 @@ ScreenWrapper.defaultProps = defaultProps;
export default compose(
withNavigation,
- withWindowDimensions,
withOnyx({
modal: {
key: ONYXKEYS.MODAL,
},
}),
- withNetwork(),
)(ScreenWrapper);
diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js
index 2a79f6b0a055..a083af0f2e16 100644
--- a/src/components/TextInput/BaseTextInput.js
+++ b/src/components/TextInput/BaseTextInput.js
@@ -14,7 +14,6 @@ import * as Expensicons from '../Icon/Expensicons';
import Text from '../Text';
import * as styleConst from './styleConst';
import * as StyleUtils from '../../styles/StyleUtils';
-import variables from '../../styles/variables';
import getSecureEntryKeyboardType from '../../libs/getSecureEntryKeyboardType';
class BaseTextInput extends Component {
@@ -31,7 +30,6 @@ class BaseTextInput extends Component {
passwordHidden: props.secureTextEntry,
textInputWidth: 0,
prefixWidth: 0,
- height: variables.componentSizeLarge,
// Value should be kept in state for the autoGrow feature to work - https://github.com/Expensify/App/pull/8232#issuecomment-1077282006
value,
@@ -214,7 +212,6 @@ class BaseTextInput extends Component {
>
this.setState({height: event.nativeEvent.layout.height})}
style={[
textInputContainerStyles,
@@ -267,7 +264,6 @@ class BaseTextInput extends Component {
!hasLabel && styles.pv0,
this.props.prefixCharacter && StyleUtils.getPaddingLeft(this.state.prefixWidth + styles.pl1.paddingLeft),
this.props.secureTextEntry && styles.secureInput,
- {height: this.state.height},
]}
multiline={this.props.multiline}
maxLength={this.props.maxLength}
diff --git a/src/languages/en.js b/src/languages/en.js
index fa86c37a450d..204efa8fb27d 100755
--- a/src/languages/en.js
+++ b/src/languages/en.js
@@ -393,7 +393,6 @@ export default {
notOwnerOfFund: 'There was an error setting this card as your default payment method.',
setDefaultFailure: 'Something went wrong. Please chat with Concierge for further assistance.',
},
- addBankAccountSuccess: 'Your bank account has successfully been added.',
addBankAccountFailure: 'And unexpected error occurred while trying to add your bank account. Please try again.',
},
transferAmountPage: {
@@ -569,6 +568,8 @@ export default {
enterPassword: 'Enter Expensify password',
alreadyAdded: 'This account has already been added.',
chooseAccountLabel: 'Account',
+ successTitle: 'Personal bank account added!',
+ successMessage: 'Congrats, your bank account is set up and ready to receive reimbursements.',
},
attachmentView: {
unknownFilename: 'Unknown filename',
diff --git a/src/languages/es.js b/src/languages/es.js
index 934df4f03e1c..ea065baa7f43 100644
--- a/src/languages/es.js
+++ b/src/languages/es.js
@@ -393,7 +393,6 @@ export default {
notOwnerOfFund: 'Ha ocurrido un error al establecer esta tarjeta de crédito como tu método de pago predeterminado.',
setDefaultFailure: 'No se ha podido configurar el método de pago.',
},
- addBankAccountSuccess: 'Su cuenta bancaria ha sido añadida con éxito.',
addBankAccountFailure: 'Y ocurrió un error inesperado al intentar agregar su cuenta bancaria. Inténtalo de nuevo.',
},
transferAmountPage: {
@@ -569,6 +568,8 @@ export default {
enterPassword: 'Escribe tu contraseña de Expensify',
alreadyAdded: 'Esta cuenta ya ha sido agregada.',
chooseAccountLabel: 'Cuenta',
+ successTitle: '¡Cuenta bancaria personal añadida!',
+ successMessage: 'Enhorabuena, tu cuenta bancaria está lista para recibir reembolsos.',
},
attachmentView: {
unknownFilename: 'Archivo desconocido',
diff --git a/src/libs/Navigation/CustomActions.js b/src/libs/Navigation/DeprecatedCustomActions.js
similarity index 99%
rename from src/libs/Navigation/CustomActions.js
rename to src/libs/Navigation/DeprecatedCustomActions.js
index c5fb5f2a4154..1c192d60f93e 100644
--- a/src/libs/Navigation/CustomActions.js
+++ b/src/libs/Navigation/DeprecatedCustomActions.js
@@ -16,6 +16,7 @@ function getActiveState() {
/**
* Go back to the Main Drawer
+ * @deprecated
* @param {Object} navigationRef
*/
function navigateBackToRootDrawer() {
@@ -64,6 +65,7 @@ function getScreenNameFromState(state) {
*
* More context here: https://github.com/react-navigation/react-navigation/issues/9744
*
+ * @deprecated
* @param {String} route
* @returns {Function}
*/
diff --git a/src/libs/Navigation/Navigation.js b/src/libs/Navigation/Navigation.js
index c19f10c635d8..6891244a2760 100644
--- a/src/libs/Navigation/Navigation.js
+++ b/src/libs/Navigation/Navigation.js
@@ -5,7 +5,7 @@ import Onyx from 'react-native-onyx';
import Log from '../Log';
import linkTo from './linkTo';
import ROUTES from '../../ROUTES';
-import CustomActions from './CustomActions';
+import DeprecatedCustomActions from './DeprecatedCustomActions';
import ONYXKEYS from '../../ONYXKEYS';
import linkingConfig from './linkingConfig';
import navigationRef from './navigationRef';
@@ -140,7 +140,7 @@ function navigate(route = ROUTES.HOME) {
}
if (isDrawerRoute(route)) {
- navigationRef.current.dispatch(CustomActions.pushDrawerRoute(route));
+ navigationRef.current.dispatch(DeprecatedCustomActions.pushDrawerRoute(route));
return;
}
@@ -161,7 +161,7 @@ function dismissModal(shouldOpenDrawer = false) {
? shouldOpenDrawer
: false;
- CustomActions.navigateBackToRootDrawer();
+ DeprecatedCustomActions.navigateBackToRootDrawer();
if (normalizedShouldOpenDrawer) {
openDrawer();
}
diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js
index 53ae93cf8b74..ad98e8fac8f5 100644
--- a/src/libs/OptionsListUtils.js
+++ b/src/libs/OptionsListUtils.js
@@ -89,10 +89,6 @@ Onyx.connect({
},
});
-// We are initializing a default avatar here so that we use the same default color for each user we are inviting. This
-// will update when the OptionsListUtils re-loads. But will stay the same color for the life of the JS session.
-const defaultAvatarForUserToInvite = ReportUtils.getDefaultAvatar();
-
/**
* Adds expensify SMS domain (@expensify.sms) if login is a phone number and if it's not included yet
*
@@ -591,7 +587,7 @@ function getOptions(reports, personalDetails, activeReportID, {
userToInvite = createOption([login], personalDetails, null, {
showChatPreviewLine,
});
- userToInvite.icons = [defaultAvatarForUserToInvite];
+ userToInvite.icons = [ReportUtils.getDefaultAvatar(login)];
}
// If we are prioritizing 1:1 chats in search, do it only once we started searching
diff --git a/src/libs/actions/BankAccounts.js b/src/libs/actions/BankAccounts.js
index 1c9ed327a1d1..8fb86b3c25dd 100644
--- a/src/libs/actions/BankAccounts.js
+++ b/src/libs/actions/BankAccounts.js
@@ -85,7 +85,7 @@ function addPersonalBankAccount(account, password) {
value: {
loading: false,
error: '',
- success: Localize.translateLocal('paymentsPage.addBankAccountSuccess'),
+ shouldShowSuccess: true,
},
},
],
diff --git a/src/libs/getAvatarDefaultSource/index.js b/src/libs/getAvatarDefaultSource/index.js
new file mode 100644
index 000000000000..54c2a7d04b01
--- /dev/null
+++ b/src/libs/getAvatarDefaultSource/index.js
@@ -0,0 +1,7 @@
+/**
+ * Avatar icon flickers when message is sent for the first time, return and set the source as
+ * defaultSource prop of image to prevent avatar icon from flicker when running on Web/Desktop
+ * @param {String|Function} source The source of avatar image
+ * @return {Object} The image source
+ */
+export default source => ({uri: source});
diff --git a/src/libs/getAvatarDefaultSource/index.native.js b/src/libs/getAvatarDefaultSource/index.native.js
new file mode 100644
index 000000000000..1c1c79caf151
--- /dev/null
+++ b/src/libs/getAvatarDefaultSource/index.native.js
@@ -0,0 +1,5 @@
+/**
+ * Avatar icon does not flicker when running on Native, return and set undefined as defaultSource prop of image
+ * @return {Object} undefined
+ */
+export default () => undefined;
diff --git a/src/pages/AddPersonalBankAccountPage.js b/src/pages/AddPersonalBankAccountPage.js
index 6d6586fa1f0e..dc5245702c88 100644
--- a/src/pages/AddPersonalBankAccountPage.js
+++ b/src/pages/AddPersonalBankAccountPage.js
@@ -15,7 +15,11 @@ import compose from '../libs/compose';
import ONYXKEYS from '../ONYXKEYS';
import Text from '../components/Text';
import styles from '../styles/styles';
+import * as Illustrations from '../components/Icon/Illustrations';
+import Icon from '../components/Icon';
+import defaultTheme from '../styles/themes/default';
import Button from '../components/Button';
+import FixedFooter from '../components/FixedFooter';
import FormScrollView from '../components/FormScrollView';
import FormAlertWithSubmitButton from '../components/FormAlertWithSubmitButton';
import FormHelper from '../libs/FormHelper';
@@ -28,7 +32,7 @@ const propTypes = {
...withLocalizePropTypes,
personalBankAccount: PropTypes.shape({
error: PropTypes.string,
- success: PropTypes.string,
+ shouldShowSuccess: PropTypes.bool,
loading: PropTypes.bool,
}),
};
@@ -36,7 +40,7 @@ const propTypes = {
const defaultProps = {
personalBankAccount: {
error: '',
- success: '',
+ shouldShowSuccess: false,
loading: false,
},
};
@@ -118,7 +122,7 @@ class AddPersonalBankAccountPage extends React.Component {
}
render() {
- const success = lodashGet(this.props, 'personalBankAccount.success', '');
+ const shouldShowSuccess = lodashGet(this.props, 'personalBankAccount.shouldShowSuccess', false);
const error = lodashGet(this.props, 'personalBankAccount.error', '');
const loading = lodashGet(this.props, 'personalBankAccount.loading', false);
@@ -130,18 +134,33 @@ class AddPersonalBankAccountPage extends React.Component {
shouldShowBackButton
onBackButtonPress={Navigation.goBack}
/>
- {success ? (
+ {shouldShowSuccess ? (
<>
-
- {success}
-
-
+
+
+
+
+ {this.props.translate('addPersonalBankAccountPage.successTitle')}
+
+
+ {this.props.translate('addPersonalBankAccountPage.successMessage')}
+
+
+
+
+
>
) : (
diff --git a/src/pages/EnablePayments/AdditionalDetailsStep.js b/src/pages/EnablePayments/AdditionalDetailsStep.js
index 17527d96b337..71daa5f13f72 100644
--- a/src/pages/EnablePayments/AdditionalDetailsStep.js
+++ b/src/pages/EnablePayments/AdditionalDetailsStep.js
@@ -3,7 +3,9 @@ import _ from 'underscore';
import React from 'react';
import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
-import {View} from 'react-native';
+import {
+ View, KeyboardAvoidingView,
+} from 'react-native';
import IdologyQuestions from './IdologyQuestions';
import ScreenWrapper from '../../components/ScreenWrapper';
import HeaderWithCloseButton from '../../components/HeaderWithCloseButton';
@@ -252,17 +254,19 @@ class AdditionalDetailsStep extends React.Component {
render() {
if (!_.isEmpty(this.props.walletAdditionalDetails.questions)) {
return (
-
- Navigation.dismissModal()}
- shouldShowBackButton
- onBackButtonPress={() => Wallet.setAdditionalDetailsQuestions(null)}
- />
-
+
+
+ Navigation.dismissModal()}
+ shouldShowBackButton
+ onBackButtonPress={() => Wallet.setAdditionalDetailsQuestions(null)}
+ />
+
+
);
}
@@ -273,124 +277,127 @@ class AdditionalDetailsStep extends React.Component {
const {firstName, lastName} = PersonalDetails.extractFirstAndLastNameFromAvailableDetails(this.props.currentUserPersonalDetails);
return (
-
- Navigation.dismissModal()}
- />
-
-
- {this.props.translate('additionalDetailsStep.helpText')}
-
- {this.props.translate('additionalDetailsStep.helpLink')}
-
-
- this.form = el}>
-
-
+
+
+ Navigation.dismissModal()}
+ />
+
+
+ {this.props.translate('additionalDetailsStep.helpText')}
+
+ {this.props.translate('additionalDetailsStep.helpLink')}
+
+
+ this.form = el}>
+
+
+ this.clearErrorAndSetValue('legalFirstName', val)}
+ value={this.props.walletAdditionalDetailsDraft.legalFirstName || firstName}
+ errorText={this.getErrorText('legalFirstName')}
+ />
+ this.clearErrorAndSetValue('legalLastName', val)}
+ value={this.props.walletAdditionalDetailsDraft.legalLastName || lastName}
+ errorText={this.getErrorText('legalLastName')}
+ />
+ {
+ const renamedFields = {
+ street: 'addressStreet',
+ state: 'addressState',
+ zipCode: 'addressZip',
+ city: 'addressCity',
+ };
+ _.each(values, (value, inputKey) => {
+ const renamedInputKey = lodashGet(renamedFields, inputKey, inputKey);
+ this.clearErrorAndSetValue(renamedInputKey, value);
+ });
+ }}
+ errorText={this.getErrorText('addressStreet')}
+ hint={this.props.translate('common.noPO')}
+ />
+ {this.props.walletAdditionalDetailsDraft.addressStreet ? (
+ <>
+ {/** Once the user has started entering his address, show the other address fields (city, state, zip) */}
+ {/** We'll autofill them when the user selects a full address from the google autocomplete */}
+ this.clearErrorAndSetValue('addressCity', val)}
+ value={this.props.walletAdditionalDetailsDraft.addressCity || ''}
+ errorText={this.getErrorText('addressCity')}
+ />
+ this.clearErrorAndSetValue('addressState', val)}
+ value={this.props.walletAdditionalDetailsDraft.addressState || ''}
+ errorText={this.getErrorText('addressState')}
+ />
+ this.clearErrorAndSetValue('addressZip', val)}
+ value={this.props.walletAdditionalDetailsDraft.addressZip || ''}
+ errorText={this.getErrorText('addressZip')}
+ />
+ >
+ ) : null}
+
this.clearErrorAndSetValue('legalFirstName', val)}
- value={this.props.walletAdditionalDetailsDraft.legalFirstName || firstName}
- errorText={this.getErrorText('legalFirstName')}
+ keyboardType={CONST.KEYBOARD_TYPE.PHONE_PAD}
+ label={this.props.translate(this.fieldNameTranslationKeys.phoneNumber)}
+ onChangeText={val => this.clearErrorAndSetValue('phoneNumber', val)}
+ value={this.props.walletAdditionalDetailsDraft.phoneNumber || ''}
+ placeholder={this.props.translate('common.phoneNumberPlaceholder')}
+ errorText={this.getErrorText('phoneNumber')}
/>
- this.clearErrorAndSetValue('legalLastName', val)}
- value={this.props.walletAdditionalDetailsDraft.legalLastName || lastName}
- errorText={this.getErrorText('legalLastName')}
+ label={this.props.translate(this.fieldNameTranslationKeys.dob)}
+ onInputChange={val => this.clearDateErrorsAndSetValue(val)}
+ defaultValue={this.props.walletAdditionalDetailsDraft.dob || ''}
+ placeholder={this.props.translate('common.dob')}
+ errorText={this.getErrorText('dob') || this.getErrorText('age')}
+ maximumDate={new Date()}
/>
- {
- const renamedFields = {
- street: 'addressStreet',
- state: 'addressState',
- zipCode: 'addressZip',
- city: 'addressCity',
- };
- _.each(values, (value, inputKey) => {
- const renamedInputKey = lodashGet(renamedFields, inputKey, inputKey);
- this.clearErrorAndSetValue(renamedInputKey, value);
- });
- }}
- errorText={this.getErrorText('addressStreet')}
- hint={this.props.translate('common.noPO')}
+ label={this.props.translate(this.fieldNameTranslationKeys[shouldAskForFullSSN ? 'ssnFull9' : 'ssn'])}
+ onChangeText={val => this.clearSSNErrorAndSetValue(val)}
+ value={this.props.walletAdditionalDetailsDraft.ssn || ''}
+ errorText={this.getErrorText('ssnFull9') || this.getErrorText('ssn')}
+ maxLength={shouldAskForFullSSN ? 9 : 4}
+ keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD}
/>
- {this.props.walletAdditionalDetailsDraft.addressStreet ? (
- <>
- {/** Once the user has started entering his address, show the other address fields (city, state, zip) */}
- {/** We'll autofill them when the user selects a full address from the google autocomplete */}
- this.clearErrorAndSetValue('addressCity', val)}
- value={this.props.walletAdditionalDetailsDraft.addressCity || ''}
- errorText={this.getErrorText('addressCity')}
- />
- this.clearErrorAndSetValue('addressState', val)}
- value={this.props.walletAdditionalDetailsDraft.addressState || ''}
- errorText={this.getErrorText('addressState')}
- />
- this.clearErrorAndSetValue('addressZip', val)}
- value={this.props.walletAdditionalDetailsDraft.addressZip || ''}
- errorText={this.getErrorText('addressZip')}
- />
- >
- ) : null}
- this.clearErrorAndSetValue('phoneNumber', val)}
- value={this.props.walletAdditionalDetailsDraft.phoneNumber || ''}
- placeholder={this.props.translate('common.phoneNumberPlaceholder')}
- errorText={this.getErrorText('phoneNumber')}
+ {
+ this.form.scrollTo({y: 0, animated: true});
+ }}
+ message={this.props.walletAdditionalDetails.additionalErrorMessage}
+ isLoading={this.props.walletAdditionalDetails.loading}
+ buttonText={this.props.translate('common.saveAndContinue')}
/>
- this.clearDateErrorsAndSetValue(val)}
- defaultValue={this.props.walletAdditionalDetailsDraft.dob || ''}
- placeholder={this.props.translate('common.dob')}
- errorText={this.getErrorText('dob') || this.getErrorText('age')}
- maximumDate={new Date()}
- />
- this.clearSSNErrorAndSetValue(val)}
- value={this.props.walletAdditionalDetailsDraft.ssn || ''}
- errorText={this.getErrorText('ssnFull9') || this.getErrorText('ssn')}
- maxLength={shouldAskForFullSSN ? 9 : 4}
- keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD}
- />
-
- {
- this.form.scrollTo({y: 0, animated: true});
- }}
- message={this.props.walletAdditionalDetails.additionalErrorMessage}
- isLoading={this.props.walletAdditionalDetails.loading}
- buttonText={this.props.translate('common.saveAndContinue')}
- />
-
-
+
+
+
);
}
diff --git a/src/pages/EnablePayments/EnablePaymentsPage.js b/src/pages/EnablePayments/EnablePaymentsPage.js
index 53f25dfeffd7..f1ac80846a05 100644
--- a/src/pages/EnablePayments/EnablePaymentsPage.js
+++ b/src/pages/EnablePayments/EnablePaymentsPage.js
@@ -1,6 +1,7 @@
import _ from 'underscore';
import React from 'react';
import {withOnyx} from 'react-native-onyx';
+import {KeyboardAvoidingView} from 'react-native';
import ScreenWrapper from '../../components/ScreenWrapper';
import * as BankAccounts from '../../libs/actions/BankAccounts';
import ONYXKEYS from '../../ONYXKEYS';
@@ -58,12 +59,14 @@ class EnablePaymentsPage extends React.Component {
if (this.props.userWallet.shouldShowFailedKYC) {
return (
-
- Navigation.dismissModal()}
- />
-
+
+
+ Navigation.dismissModal()}
+ />
+
+
);
}
diff --git a/src/pages/GetAssistancePage.js b/src/pages/GetAssistancePage.js
index 4a07075b0d3f..030ea26bac8a 100644
--- a/src/pages/GetAssistancePage.js
+++ b/src/pages/GetAssistancePage.js
@@ -3,6 +3,7 @@ import {View} from 'react-native';
import PropTypes from 'prop-types';
import ScreenWrapper from '../components/ScreenWrapper';
import withLocalize, {withLocalizePropTypes} from '../components/withLocalize';
+import KeyboardAvoidingView from '../components/KeyboardAvoidingView';
import HeaderWithCloseButton from '../components/HeaderWithCloseButton';
import Section from '../components/Section';
import Navigation from '../libs/Navigation/Navigation';
@@ -27,34 +28,36 @@ const propTypes = {
const GetAssistancePage = props => (
- Navigation.dismissModal(true)}
- shouldShowBackButton
- onBackButtonPress={() => Navigation.goBack()}
- />
- Report.navigateToConciergeChat(),
- icon: Expensicons.ChatBubble,
- shouldShowRightIcon: true,
- },
- {
- title: props.translate('getAssistancePage.requestSetupCall'),
- onPress: () => Navigation.navigate(ROUTES.getRequestCallRoute(props.route.params.taskID)),
- icon: Expensicons.Phone,
- shouldShowRightIcon: true,
- },
- ]}
- >
-
- {props.translate('getAssistancePage.description')}
-
-
+
+ Navigation.dismissModal(true)}
+ shouldShowBackButton
+ onBackButtonPress={() => Navigation.goBack()}
+ />
+ Report.navigateToConciergeChat(),
+ icon: Expensicons.ChatBubble,
+ shouldShowRightIcon: true,
+ },
+ {
+ title: props.translate('getAssistancePage.requestSetupCall'),
+ onPress: () => Navigation.navigate(ROUTES.getRequestCallRoute(props.route.params.taskID)),
+ icon: Expensicons.Phone,
+ shouldShowRightIcon: true,
+ },
+ ]}
+ >
+
+ {props.translate('getAssistancePage.description')}
+
+
+
);
diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js
index d8bb8fe7fa4e..0e55bdbccfbc 100755
--- a/src/pages/NewChatPage.js
+++ b/src/pages/NewChatPage.js
@@ -16,6 +16,7 @@ import ScreenWrapper from '../components/ScreenWrapper';
import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator';
import withLocalize, {withLocalizePropTypes} from '../components/withLocalize';
import compose from '../libs/compose';
+import KeyboardAvoidingView from '../components/KeyboardAvoidingView';
import personalDetailsPropType from './personalDetailsPropType';
const propTypes = {
@@ -212,7 +213,7 @@ class NewChatPage extends Component {
return (
{({didScreenTransitionEnd}) => (
- <>
+
)}
- >
+
)}
);
diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js
index 9f022bd00bc4..21a15777b738 100644
--- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js
+++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js
@@ -14,6 +14,7 @@ import CONST from '../../CONST';
import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize';
import compose from '../../libs/compose';
import styles from '../../styles/styles';
+import KeyboardAvoidingView from '../../components/KeyboardAvoidingView';
import getPlaidOAuthReceivedRedirectURI from '../../libs/getPlaidOAuthReceivedRedirectURI';
import Text from '../../components/Text';
import {withNetwork} from '../../components/OnyxProvider';
@@ -218,32 +219,34 @@ class ReimbursementAccountPage extends React.Component {
}
return (
- {currentStep === CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT && (
-
- )}
- {currentStep === CONST.BANK_ACCOUNT.STEP.COMPANY && (
-
- )}
- {currentStep === CONST.BANK_ACCOUNT.STEP.REQUESTOR && (
-
- )}
- {currentStep === CONST.BANK_ACCOUNT.STEP.ACH_CONTRACT && (
-
- )}
- {currentStep === CONST.BANK_ACCOUNT.STEP.VALIDATION && (
-
- )}
- {currentStep === CONST.BANK_ACCOUNT.STEP.ENABLE && (
-
- )}
-
+
+ {currentStep === CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT && (
+
+ )}
+ {currentStep === CONST.BANK_ACCOUNT.STEP.COMPANY && (
+
+ )}
+ {currentStep === CONST.BANK_ACCOUNT.STEP.REQUESTOR && (
+
+ )}
+ {currentStep === CONST.BANK_ACCOUNT.STEP.ACH_CONTRACT && (
+
+ )}
+ {currentStep === CONST.BANK_ACCOUNT.STEP.VALIDATION && (
+
+ )}
+ {currentStep === CONST.BANK_ACCOUNT.STEP.ENABLE && (
+
+ )}
+
+
);
}
diff --git a/src/pages/RequestCallPage.js b/src/pages/RequestCallPage.js
index d2b8fa817e8c..cbfd52a4390c 100644
--- a/src/pages/RequestCallPage.js
+++ b/src/pages/RequestCallPage.js
@@ -23,6 +23,7 @@ import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes,
import TextInput from '../components/TextInput';
import Text from '../components/Text';
import Section from '../components/Section';
+import KeyboardAvoidingView from '../components/KeyboardAvoidingView';
import * as Illustrations from '../components/Icon/Illustrations';
import * as Expensicons from '../components/Icon/Expensicons';
import * as LoginUtils from '../libs/LoginUtils';
@@ -284,84 +285,86 @@ class RequestCallPage extends Component {
return (
- Navigation.goBack()}
- onCloseButtonPress={() => Navigation.dismissModal(true)}
- />
- {this.props.requestCallForm.didRequestCallSucceed
- ? (
-
- ) : (
- <>
-
-
-
-
- {this.props.translate('requestCallPage.description')}
+
+ Navigation.goBack()}
+ onCloseButtonPress={() => Navigation.dismissModal(true)}
+ />
+ {this.props.requestCallForm.didRequestCallSucceed
+ ? (
+
+ ) : (
+ <>
+
+
+
+
+ {this.props.translate('requestCallPage.description')}
+
+ this.setState({firstName})}
+ onChangeLastName={lastName => this.setState({lastName})}
+ style={[styles.mv4]}
+ />
+ this.setState({phoneNumber})}
+ />
+ this.setState({phoneExtension})}
+ containerStyles={[styles.mt4]}
+ />
+ {this.getWaitTimeMessage()}
+
+
+
+
+ {isBlockedFromConcierge && (
+
+
+ {this.props.translate('requestCallPage.blockedFromConcierge')}
+
+ )}
+ {!_.isEmpty(this.props.requestCallForm.error) && (
+
+ {this.props.requestCallForm.error}
- this.setState({firstName})}
- onChangeLastName={lastName => this.setState({lastName})}
- style={[styles.mv4]}
- />
- this.setState({phoneNumber})}
- />
- this.setState({phoneExtension})}
- containerStyles={[styles.mt4]}
- />
- {this.getWaitTimeMessage()}
-
-
-
-
- {isBlockedFromConcierge && (
-
-
- {this.props.translate('requestCallPage.blockedFromConcierge')}
-
- )}
- {!_.isEmpty(this.props.requestCallForm.error) && (
-
- {this.props.requestCallForm.error}
-
- )}
-
-
- >
- )}
+ )}
+
+
+ >
+ )}
+
);
}
diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js
index 4cc4589ec04d..84064086fe64 100644
--- a/src/pages/home/ReportScreen.js
+++ b/src/pages/home/ReportScreen.js
@@ -16,6 +16,7 @@ import Permissions from '../../libs/Permissions';
import * as ReportUtils from '../../libs/ReportUtils';
import ReportActionsView from './report/ReportActionsView';
import ReportActionCompose from './report/ReportActionCompose';
+import KeyboardAvoidingView from '../../components/KeyboardAvoidingView';
import SwipeableView from '../../components/SwipeableView';
import CONST from '../../CONST';
import FullScreenLoadingIndicator from '../../components/FullscreenLoadingIndicator';
@@ -214,49 +215,51 @@ class ReportScreen extends React.Component {
return (
- Navigation.navigate(ROUTES.HOME)}
- />
-
-
- {this.shouldShowLoader() && }
- {!this.shouldShowLoader() && (
-
- )}
- {(isArchivedRoom || this.props.session.shouldShowComposeInput) && (
-
- {
- isArchivedRoom
- ? (
-
- ) : (
-
-
-
- )
- }
+
+ Navigation.navigate(ROUTES.HOME)}
+ />
+
+
+ {this.shouldShowLoader() && }
+ {!this.shouldShowLoader() && (
+
+ )}
+ {(isArchivedRoom || this.props.session.shouldShowComposeInput) && (
+
+ {
+ isArchivedRoom
+ ? (
+
+ ) : (
+
+
+
+ )
+ }
+
+ )}
- )}
-
+
);
}
diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js
index c2cf3b9262b3..fd45bd61626b 100755
--- a/src/pages/home/report/ReportActionCompose.js
+++ b/src/pages/home/report/ReportActionCompose.js
@@ -669,7 +669,7 @@ class ReportActionCompose extends React.Component {
- {!this.props.isSmallScreenWidth && }
+
diff --git a/src/pages/home/report/ReportActionItemFragment.js b/src/pages/home/report/ReportActionItemFragment.js
index 964142ca5b95..ae62f0010789 100644
--- a/src/pages/home/report/ReportActionItemFragment.js
+++ b/src/pages/home/report/ReportActionItemFragment.js
@@ -14,6 +14,7 @@ import withWindowDimensions, {windowDimensionsPropTypes} from '../../../componen
import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
import canUseTouchScreen from '../../../libs/canUseTouchscreen';
import compose from '../../../libs/compose';
+import * as StyleUtils from '../../../styles/StyleUtils';
const propTypes = {
/** The message fragment needing to be displayed */
@@ -117,16 +118,16 @@ const ReportActionItemFragment = (props) => {
return (
- {Str.htmlDecode(text)}
+ {StyleUtils.convertToLTR(Str.htmlDecode(text))}
{props.fragment.isEdited && (
-
- {` ${props.translate('reportActionCompose.edited')}`}
-
+
+ {` ${props.translate('reportActionCompose.edited')}`}
+
)}
);
diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js
index d0f9861ee819..b8b408b4c815 100644
--- a/src/pages/iou/IOUCurrencySelection.js
+++ b/src/pages/iou/IOUCurrencySelection.js
@@ -10,6 +10,7 @@ import ScreenWrapper from '../../components/ScreenWrapper';
import HeaderWithCloseButton from '../../components/HeaderWithCloseButton';
import compose from '../../libs/compose';
import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize';
+import KeyboardAvoidingView from '../../components/KeyboardAvoidingView';
import * as IOU from '../../libs/actions/IOU';
import * as CurrencySymbolUtils from '../../libs/CurrencySymbolUtils';
import {withNetwork} from '../../components/OnyxProvider';
@@ -116,19 +117,21 @@ class IOUCurrencySelection extends Component {
const headerMessage = this.state.searchValue.trim() && !this.state.currencyData.length ? this.props.translate('common.noResultsFound') : '';
return (
-
-
+
+
+
+
);
}
diff --git a/src/pages/iou/IOUModal.js b/src/pages/iou/IOUModal.js
index 508456ebe9a4..14a24b473aad 100755
--- a/src/pages/iou/IOUModal.js
+++ b/src/pages/iou/IOUModal.js
@@ -23,6 +23,7 @@ import AnimatedStep from '../../components/AnimatedStep';
import ScreenWrapper from '../../components/ScreenWrapper';
import Tooltip from '../../components/Tooltip';
import CONST from '../../CONST';
+import KeyboardAvoidingView from '../../components/KeyboardAvoidingView';
import * as PersonalDetails from '../../libs/actions/PersonalDetails';
import withCurrentUserPersonalDetails from '../../components/withCurrentUserPersonalDetails';
import ROUTES from '../../ROUTES';
@@ -173,7 +174,7 @@ class IOUModal extends Component {
* Decides our animation type based on whether we're increasing or decreasing
* our step index.
* @returns {String}
- */
+ */
getDirection() {
if (this.state.previousStepIndex < this.state.currentStepIndex) {
return 'in';
@@ -357,7 +358,7 @@ class IOUModal extends Component {
return (
{({didScreenTransitionEnd}) => (
- <>
+
)}
- >
+
)}
);
diff --git a/src/pages/settings/AddSecondaryLoginPage.js b/src/pages/settings/AddSecondaryLoginPage.js
index afffdb7cfdbd..7a15526de572 100755
--- a/src/pages/settings/AddSecondaryLoginPage.js
+++ b/src/pages/settings/AddSecondaryLoginPage.js
@@ -14,6 +14,7 @@ import ONYXKEYS from '../../ONYXKEYS';
import Button from '../../components/Button';
import ROUTES from '../../ROUTES';
import CONST from '../../CONST';
+import KeyboardAvoidingView from '../../components/KeyboardAvoidingView';
import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize';
import compose from '../../libs/compose';
import FixedFooter from '../../components/FixedFooter';
@@ -100,60 +101,62 @@ class AddSecondaryLoginPage extends Component {
this.phoneNumberInputRef.focus();
}}
>
- Navigation.navigate(ROUTES.SETTINGS_PROFILE)}
- onCloseButtonPress={() => Navigation.dismissModal()}
- />
-
-
- {this.props.translate(this.formType === CONST.LOGIN_TYPE.PHONE
- ? 'addSecondaryLoginPage.enterPreferredPhoneNumberToSendValidationLink'
- : 'addSecondaryLoginPage.enterPreferredEmailToSendValidationLink')}
-
-
- this.phoneNumberInputRef = el}
- value={this.state.login}
- onChangeText={this.onSecondaryLoginChange}
- keyboardType={this.formType === CONST.LOGIN_TYPE.PHONE
- ? CONST.KEYBOARD_TYPE.PHONE_PAD : undefined}
- returnKeyType="done"
- />
-
-
- this.setState({password})}
- secureTextEntry
- autoCompleteType="password"
- textContentType="password"
- onSubmitEditing={this.submitForm}
- />
-
- {!_.isEmpty(this.props.user.error) && (
-
- {this.props.user.error}
-
- )}
-
-
-
+
+
+ {this.props.translate(this.formType === CONST.LOGIN_TYPE.PHONE
+ ? 'addSecondaryLoginPage.enterPreferredPhoneNumberToSendValidationLink'
+ : 'addSecondaryLoginPage.enterPreferredEmailToSendValidationLink')}
+
+
+ this.phoneNumberInputRef = el}
+ value={this.state.login}
+ onChangeText={this.onSecondaryLoginChange}
+ keyboardType={this.formType === CONST.LOGIN_TYPE.PHONE
+ ? CONST.KEYBOARD_TYPE.PHONE_PAD : undefined}
+ returnKeyType="done"
+ />
+
+
+ this.setState({password})}
+ secureTextEntry
+ autoCompleteType="password"
+ textContentType="password"
+ onSubmitEditing={this.submitForm}
+ />
+
+ {!_.isEmpty(this.props.user.error) && (
+
+ {this.props.user.error}
+
+ )}
+
+
+
+
+
);
}
diff --git a/src/pages/settings/PasswordPage.js b/src/pages/settings/PasswordPage.js
index 69270c2bacaf..23d70132c056 100755
--- a/src/pages/settings/PasswordPage.js
+++ b/src/pages/settings/PasswordPage.js
@@ -14,6 +14,7 @@ import * as ValidationUtils from '../../libs/ValidationUtils';
import * as User from '../../libs/actions/User';
import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize';
import compose from '../../libs/compose';
+import KeyboardAvoidingView from '../../components/KeyboardAvoidingView';
import FixedFooter from '../../components/FixedFooter';
import TextInput from '../../components/TextInput';
import * as Session from '../../libs/actions/Session';
@@ -146,84 +147,86 @@ class PasswordPage extends Component {
this.currentPasswordInputRef.focus();
}}
>
- Navigation.goBack()}
- onCloseButtonPress={() => Navigation.dismissModal(true)}
- />
- {!_.isEmpty(this.props.account.success)
- ? (
-
- ) : (
- <>
-
-
- {this.props.translate('passwordPage.changingYourPasswordPrompt')}
-
-
- this.currentPasswordInputRef = el}
- secureTextEntry
- autoCompleteType="password"
- textContentType="password"
- value={this.state.currentPassword}
- onChangeText={text => this.clearErrorAndSetValue('currentPassword', text)}
- returnKeyType="done"
- hasError={this.state.errors.currentPassword}
- errorText={this.getErrorText('currentPassword')}
- onSubmitEditing={this.submit}
- />
-
-
- this.clearErrorAndSetValue('newPassword', text, ['newPasswordSameAsOld'])}
- onSubmitEditing={this.submit}
- />
- {shouldShowNewPasswordPrompt && (
-
- {this.props.translate('passwordPage.newPasswordPrompt')}
+
+ Navigation.goBack()}
+ onCloseButtonPress={() => Navigation.dismissModal(true)}
+ />
+ {!_.isEmpty(this.props.account.success)
+ ? (
+
+ ) : (
+ <>
+
+
+ {this.props.translate('passwordPage.changingYourPasswordPrompt')}
+
+ this.currentPasswordInputRef = el}
+ secureTextEntry
+ autoCompleteType="password"
+ textContentType="password"
+ value={this.state.currentPassword}
+ onChangeText={text => this.clearErrorAndSetValue('currentPassword', text)}
+ returnKeyType="done"
+ hasError={this.state.errors.currentPassword}
+ errorText={this.getErrorText('currentPassword')}
+ onSubmitEditing={this.submit}
+ />
+
+
+ this.clearErrorAndSetValue('newPassword', text, ['newPasswordSameAsOld'])}
+ onSubmitEditing={this.submit}
+ />
+ {shouldShowNewPasswordPrompt && (
+
+ {this.props.translate('passwordPage.newPasswordPrompt')}
+
+ )}
+
+ {_.every(this.state.errors, error => !error) && !_.isEmpty(this.props.account.error) && (
+
+ {this.props.account.error}
+
)}
-
- {_.every(this.state.errors, error => !error) && !_.isEmpty(this.props.account.error) && (
-
- {this.props.account.error}
-
- )}
-
-
-
-
- >
- )}
+
+
+
+
+ >
+ )}
+
);
}
diff --git a/src/pages/settings/Payments/AddDebitCardPage.js b/src/pages/settings/Payments/AddDebitCardPage.js
index b59619f8e052..9e1ccee29c72 100644
--- a/src/pages/settings/Payments/AddDebitCardPage.js
+++ b/src/pages/settings/Payments/AddDebitCardPage.js
@@ -9,6 +9,7 @@ import Text from '../../../components/Text';
import TextLink from '../../../components/TextLink';
import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
import * as PaymentMethods from '../../../libs/actions/PaymentMethods';
+import KeyboardAvoidingView from '../../../components/KeyboardAvoidingView';
import * as ValidationUtils from '../../../libs/ValidationUtils';
import CheckboxWithLabel from '../../../components/CheckboxWithLabel';
import StatePicker from '../../../components/StatePicker';
@@ -87,92 +88,94 @@ class DebitCardPage extends Component {
render() {
return (
- Navigation.goBack()}
- onCloseButtonPress={() => Navigation.dismissModal(true)}
- />
-
+
+
);
}
diff --git a/src/pages/settings/Payments/AddPayPalMePage.js b/src/pages/settings/Payments/AddPayPalMePage.js
index 2e8bdff1d8e7..fde6b501547a 100644
--- a/src/pages/settings/Payments/AddPayPalMePage.js
+++ b/src/pages/settings/Payments/AddPayPalMePage.js
@@ -13,6 +13,7 @@ import styles from '../../../styles/styles';
import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
import compose from '../../../libs/compose';
import Button from '../../../components/Button';
+import KeyboardAvoidingView from '../../../components/KeyboardAvoidingView';
import FixedFooter from '../../../components/FixedFooter';
import Growl from '../../../libs/Growl';
import TextInput from '../../../components/TextInput';
@@ -60,42 +61,44 @@ class AddPayPalMePage extends React.Component {
render() {
return (
- Navigation.navigate(ROUTES.SETTINGS_PAYMENTS)}
- onCloseButtonPress={() => Navigation.dismissModal(true)}
- />
-
-
-
- {this.props.translate('addPayPalMePage.enterYourUsernameToGetPaidViaPayPal')}
-
- this.setState({payPalMeUsername: text, payPalMeUsernameError: false})}
- returnKeyType="done"
- hasError={this.state.payPalMeUsernameError}
- errorText={this.state.payPalMeUsernameError ? this.props.translate('addPayPalMePage.formatError') : ''}
- />
-
-
-
-
+
+
+
+ {this.props.translate('addPayPalMePage.enterYourUsernameToGetPaidViaPayPal')}
+
+ this.setState({payPalMeUsername: text, payPalMeUsernameError: false})}
+ returnKeyType="done"
+ hasError={this.state.payPalMeUsernameError}
+ errorText={this.state.payPalMeUsernameError ? this.props.translate('addPayPalMePage.formatError') : ''}
+ />
+
+
+
+
+
+
);
}
diff --git a/src/pages/settings/Payments/ChooseTransferAccountPage.js b/src/pages/settings/Payments/ChooseTransferAccountPage.js
index 99d0b1fe2623..b1d878340048 100644
--- a/src/pages/settings/Payments/ChooseTransferAccountPage.js
+++ b/src/pages/settings/Payments/ChooseTransferAccountPage.js
@@ -5,6 +5,7 @@ import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton';
import ScreenWrapper from '../../../components/ScreenWrapper';
import Navigation from '../../../libs/Navigation/Navigation';
import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
+import KeyboardAvoidingView from '../../../components/KeyboardAvoidingView';
import CONST from '../../../CONST';
import PaymentMethodList from './PaymentMethodList';
import * as PaymentMethods from '../../../libs/actions/PaymentMethods';
@@ -57,28 +58,30 @@ const ChooseTransferAccountPage = (props) => {
return (
- Navigation.goBack()}
- onCloseButtonPress={() => Navigation.dismissModal()}
- />
-
-
+ Navigation.goBack()}
+ onCloseButtonPress={() => Navigation.dismissModal()}
/>
-
-
+
+
+
+
+
);
};
diff --git a/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js b/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js
index c8b5b803add4..779d5521732a 100644
--- a/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js
+++ b/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js
@@ -14,6 +14,7 @@ import Navigation from '../../../../libs/Navigation/Navigation';
import styles from '../../../../styles/styles';
import withLocalize from '../../../../components/withLocalize';
import compose from '../../../../libs/compose';
+import KeyboardAvoidingView from '../../../../components/KeyboardAvoidingView/index';
import * as BankAccounts from '../../../../libs/actions/BankAccounts';
import Popover from '../../../../components/Popover';
import MenuItem from '../../../../components/MenuItem';
@@ -32,7 +33,6 @@ import KYCWall from '../../../../components/KYCWall';
import {propTypes, defaultProps} from './paymentsPagePropTypes';
import {withNetwork} from '../../../../components/OnyxProvider';
import * as PaymentUtils from '../../../../libs/PaymentUtils';
-import OfflineIndicator from '../../../../components/OfflineIndicator';
class BasePaymentsPage extends React.Component {
constructor(props) {
@@ -265,176 +265,177 @@ class BasePaymentsPage extends React.Component {
const isPopoverBottomMount = this.state.anchorPositionTop === 0 || this.props.isSmallScreenWidth;
return (
- Navigation.navigate(ROUTES.SETTINGS)}
- onCloseButtonPress={() => Navigation.dismissModal(true)}
- />
-
- {Permissions.canUseWallet(this.props.betas) && (
- <>
-
-
-
- {this.props.userWallet.currentBalance > 0 && (
-
- {triggerKYCFlow => (
-
- )}
-
- )}
- >
- )}
-
- {this.props.translate('paymentsPage.paymentMethodsTitle')}
-
-
+ Navigation.navigate(ROUTES.SETTINGS)}
+ onCloseButtonPress={() => Navigation.dismissModal(true)}
/>
-
- this.addPaymentMethodTypePressed(method)}
- />
-
-
- {isPopoverBottomMount && (
-
+
+ {Permissions.canUseWallet(this.props.betas) && (
+ <>
+
+
+
+ {this.props.userWallet.currentBalance > 0 && (
+
+ {triggerKYCFlow => (
+
+ )}
+
+ )}
+ >
)}
- {shouldShowMakeDefaultButton && (
+
+ {this.props.translate('paymentsPage.paymentMethodsTitle')}
+
+
+
+ this.addPaymentMethodTypePressed(method)}
+ />
+
+
+ {isPopoverBottomMount && (
+
+ )}
+ {shouldShowMakeDefaultButton && (
+ {
+ this.setState({
+ shouldShowDefaultDeleteMenu: false,
+ });
+
+ // Wait for the previous modal to close, before opening a new one. A modal will be considered completely closed when closing animation is finished.
+ // InteractionManager fires after the currently running animation is completed.
+ // https://github.com/Expensify/App/issues/7768#issuecomment-1044879541
+ InteractionManager.runAfterInteractions(() => {
+ this.setState({
+ shouldShowPasswordPrompt: true,
+ passwordButtonText: this.props.translate('paymentsPage.setDefaultConfirmation'),
+ });
+ });
+ }}
+ style={[styles.button, styles.alignSelfCenter, styles.w100]}
+ >
+
+ {this.props.translate('paymentsPage.setDefaultConfirmation')}
+
+
+ )}
{
this.setState({
shouldShowDefaultDeleteMenu: false,
});
-
- // Wait for the previous modal to close, before opening a new one. A modal will be considered completely closed when closing animation is finished.
- // InteractionManager fires after the currently running animation is completed.
- // https://github.com/Expensify/App/issues/7768#issuecomment-1044879541
InteractionManager.runAfterInteractions(() => {
this.setState({
- shouldShowPasswordPrompt: true,
- passwordButtonText: this.props.translate('paymentsPage.setDefaultConfirmation'),
+ shouldShowConfirmPopover: true,
});
});
}}
- style={[styles.button, styles.alignSelfCenter, styles.w100]}
+ style={[
+ styles.button,
+ styles.buttonDanger,
+ shouldShowMakeDefaultButton && styles.mt4,
+ styles.alignSelfCenter,
+ styles.w100,
+ ]}
>
-
- {this.props.translate('paymentsPage.setDefaultConfirmation')}
+
+ {this.props.translate('common.delete')}
- )}
- {
- this.setState({
- shouldShowDefaultDeleteMenu: false,
- });
- InteractionManager.runAfterInteractions(() => {
- this.setState({
- shouldShowConfirmPopover: true,
- });
- });
- }}
- style={[
- styles.button,
- styles.buttonDanger,
- shouldShowMakeDefaultButton && styles.mt4,
- styles.alignSelfCenter,
- styles.w100,
- ]}
- >
-
- {this.props.translate('common.delete')}
-
-
-
-
- {
- this.hidePasswordPrompt();
- this.makeDefaultPaymentMethod(password);
- }}
- submitButtonText={this.state.passwordButtonText}
- isDangerousAction
- />
- {
- this.setState({
- shouldShowConfirmPopover: false,
- });
- this.deletePaymentMethod();
- }}
- onCancel={() => {
- this.setState({shouldShowConfirmPopover: false});
- }}
- shouldShowCancelButton
- danger
- />
-
+
+
+ {
+ this.hidePasswordPrompt();
+ this.makeDefaultPaymentMethod(password);
+ }}
+ submitButtonText={this.state.passwordButtonText}
+ isDangerousAction
+ />
+ {
+ this.setState({
+ shouldShowConfirmPopover: false,
+ });
+ this.deletePaymentMethod();
+ }}
+ onCancel={() => {
+ this.setState({shouldShowConfirmPopover: false});
+ }}
+ shouldShowCancelButton
+ danger
+ />
+
);
}
diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js
index 0cdde430ee2c..b2583dc7f2bd 100755
--- a/src/pages/settings/Profile/ProfilePage.js
+++ b/src/pages/settings/Profile/ProfilePage.js
@@ -19,6 +19,7 @@ import LoginField from './LoginField';
import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
import compose from '../../../libs/compose';
import Button from '../../../components/Button';
+import KeyboardAvoidingView from '../../../components/KeyboardAvoidingView';
import FixedFooter from '../../../components/FixedFooter';
import TextInput from '../../../components/TextInput';
import Picker from '../../../components/Picker';
@@ -218,97 +219,99 @@ class ProfilePage extends Component {
return (
- Navigation.navigate(ROUTES.SETTINGS)}
- onCloseButtonPress={() => Navigation.dismissModal(true)}
- />
-
-
+ Navigation.navigate(ROUTES.SETTINGS)}
+ onCloseButtonPress={() => Navigation.dismissModal(true)}
/>
-
- {this.props.translate('profilePage.tellUsAboutYourself')}
-
- this.setState({firstName})}
- onChangeLastName={lastName => this.setState({lastName})}
- style={[styles.mt4, styles.mb4]}
- />
-
- {
- const hasSelfSelectedPronouns = pronouns === CONST.PRONOUNS.SELF_SELECT;
- this.setState({
- pronouns: hasSelfSelectedPronouns ? '' : pronouns,
- hasSelfSelectedPronouns,
- });
- }}
- items={pronounsList}
- placeholder={{
- value: '',
- label: this.props.translate('profilePage.selectYourPronouns'),
- }}
- value={pronounsPickerValue}
+
+
- {this.state.hasSelfSelectedPronouns && (
-
- this.setState({pronouns})}
- placeholder={this.props.translate('profilePage.selfSelectYourPronoun')}
- errorText={PersonalDetails.getMaxCharacterError(this.state.hasPronounError)}
- />
-
- )}
-
-
-
-
- this.setState({selectedTimezone})}
- items={timezones}
- isDisabled={this.state.isAutomaticTimezone}
- value={this.state.selectedTimezone}
+
+ {this.props.translate('profilePage.tellUsAboutYourself')}
+
+ this.setState({firstName})}
+ onChangeLastName={lastName => this.setState({lastName})}
+ style={[styles.mt4, styles.mb4]}
/>
-
-
-
-
-
-
+
+ {
+ const hasSelfSelectedPronouns = pronouns === CONST.PRONOUNS.SELF_SELECT;
+ this.setState({
+ pronouns: hasSelfSelectedPronouns ? '' : pronouns,
+ hasSelfSelectedPronouns,
+ });
+ }}
+ items={pronounsList}
+ placeholder={{
+ value: '',
+ label: this.props.translate('profilePage.selectYourPronouns'),
+ }}
+ value={pronounsPickerValue}
+ />
+ {this.state.hasSelfSelectedPronouns && (
+
+ this.setState({pronouns})}
+ placeholder={this.props.translate('profilePage.selfSelectYourPronoun')}
+ errorText={PersonalDetails.getMaxCharacterError(this.state.hasPronounError)}
+ />
+
+ )}
+
+
+
+
+ this.setState({selectedTimezone})}
+ items={timezones}
+ isDisabled={this.state.isAutomaticTimezone}
+ value={this.state.selectedTimezone}
+ />
+
+
+
+
+
+
+
);
}
diff --git a/src/pages/settings/Security/CloseAccountPage.js b/src/pages/settings/Security/CloseAccountPage.js
index f908faf126b9..2f9d7f5360e2 100644
--- a/src/pages/settings/Security/CloseAccountPage.js
+++ b/src/pages/settings/Security/CloseAccountPage.js
@@ -15,6 +15,7 @@ import Button from '../../../components/Button';
import Text from '../../../components/Text';
import FixedFooter from '../../../components/FixedFooter';
import ConfirmModal from '../../../components/ConfirmModal';
+import KeyboardAvoidingView from '../../../components/KeyboardAvoidingView';
import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions';
import * as CloseAccountActions from '../../../libs/actions/CloseAccount';
@@ -54,82 +55,84 @@ class CloseAccountPage extends Component {
const userEmailOrPhone = Str.removeSMSDomain(this.props.session.email);
return (
- Navigation.navigate(ROUTES.SETTINGS_SECURITY)}
- onCloseButtonPress={() => Navigation.dismissModal(true)}
- />
-
- {this.props.translate('closeAccountPage.reasonForLeavingPrompt')}
- this.setState({reasonForLeaving})}
- label={this.props.translate('closeAccountPage.enterMessageHere')}
- containerStyles={[styles.mt5, styles.closeAccountMessageInput]}
+
+ Navigation.navigate(ROUTES.SETTINGS_SECURITY)}
+ onCloseButtonPress={() => Navigation.dismissModal(true)}
/>
-
-
- {this.props.translate('closeAccountPage.closeAccountWarning')}
-
- {' '}
- {this.props.translate('closeAccountPage.closeAccountPermanentlyDeleteData')}
-
-
-
- {this.props.translate('closeAccountPage.defaultContact')}
-
- {' '}
- {userEmailOrPhone}
-
- this.setState({phoneOrEmail: phoneOrEmail.toLowerCase()})}
- label={this.props.translate('closeAccountPage.enterDefaultContact')}
- containerStyles={[styles.mt5]}
- />
-
-
-
-
- {this.props.translate('closeAccountPage.closeAccountActionRequired')}
+
+ {this.props.translate('closeAccountPage.reasonForLeavingPrompt')}
+ this.setState({reasonForLeaving})}
+ label={this.props.translate('closeAccountPage.enterMessageHere')}
+ containerStyles={[styles.mt5, styles.closeAccountMessageInput]}
+ />
+
+
+ {this.props.translate('closeAccountPage.closeAccountWarning')}
+
{' '}
- { Linking.openURL('https://community.expensify.com/discussion/4724/faq-why-cant-i-close-my-account'); }}
- >
- {this.props.translate('common.here')}
+ {this.props.translate('closeAccountPage.closeAccountPermanentlyDeleteData')}
+
+
+
+ {this.props.translate('closeAccountPage.defaultContact')}
{' '}
- {this.props.translate('closeAccountPage.closeAccountTryAgainAfter')}
+ {userEmailOrPhone}
- )}
- onConfirm={CloseAccountActions.hideCloseAccountModal}
- isVisible={this.props.isCloseAccoutModalOpen}
- shouldShowCancelButton={false}
- />
+ this.setState({phoneOrEmail: phoneOrEmail.toLowerCase()})}
+ label={this.props.translate('closeAccountPage.enterDefaultContact')}
+ containerStyles={[styles.mt5]}
+ />
+
+
+
+
+ {this.props.translate('closeAccountPage.closeAccountActionRequired')}
+ {' '}
+ { Linking.openURL('https://community.expensify.com/discussion/4724/faq-why-cant-i-close-my-account'); }}
+ >
+ {this.props.translate('common.here')}
+
+ {' '}
+ {this.props.translate('closeAccountPage.closeAccountTryAgainAfter')}
+
+ )}
+ onConfirm={CloseAccountActions.hideCloseAccountModal}
+ isVisible={this.props.isCloseAccoutModalOpen}
+ shouldShowCancelButton={false}
+ />
+
);
}
diff --git a/src/pages/workspace/WorkspaceInvitePage.js b/src/pages/workspace/WorkspaceInvitePage.js
index ec3906eed06e..0940abd51d55 100644
--- a/src/pages/workspace/WorkspaceInvitePage.js
+++ b/src/pages/workspace/WorkspaceInvitePage.js
@@ -13,6 +13,7 @@ import compose from '../../libs/compose';
import ONYXKEYS from '../../ONYXKEYS';
import * as Policy from '../../libs/actions/Policy';
import TextInput from '../../components/TextInput';
+import KeyboardAvoidingView from '../../components/KeyboardAvoidingView';
import FormAlertWithSubmitButton from '../../components/FormAlertWithSubmitButton';
import OptionsSelector from '../../components/OptionsSelector';
import * as OptionsListUtils from '../../libs/OptionsListUtils';
@@ -243,7 +244,7 @@ class WorkspaceInvitePage extends React.Component {
return (
{({didScreenTransitionEnd}) => (
- <>
+
- >
+
)}
);
diff --git a/src/pages/workspace/WorkspaceNewRoomPage.js b/src/pages/workspace/WorkspaceNewRoomPage.js
index 1dae4d8d5b67..5740c74ae2e4 100644
--- a/src/pages/workspace/WorkspaceNewRoomPage.js
+++ b/src/pages/workspace/WorkspaceNewRoomPage.js
@@ -20,6 +20,7 @@ import Button from '../../components/Button';
import FixedFooter from '../../components/FixedFooter';
import Permissions from '../../libs/Permissions';
import Log from '../../libs/Log';
+import KeyboardAvoidingView from '../../components/KeyboardAvoidingView';
import * as ValidationUtils from '../../libs/ValidationUtils';
const propTypes = {
@@ -150,50 +151,52 @@ class WorkspaceNewRoomPage extends React.Component {
return (
- Navigation.dismissModal()}
- />
-
-
- this.clearErrorAndSetValue('roomName', roomName)}
- />
-
-
- this.clearErrorAndSetValue('policyID', policyID)}
- />
-
-
- this.setState({visibility})}
- />
-
-
- {_.find(visibilityOptions, option => option.value === this.state.visibility).description}
-
-
-
-
+
+
+ this.clearErrorAndSetValue('roomName', roomName)}
+ />
+
+
+ this.clearErrorAndSetValue('policyID', policyID)}
+ />
+
+
+ this.setState({visibility})}
+ />
+
+
+ {_.find(visibilityOptions, option => option.value === this.state.visibility).description}
+
+
+
+
+
+
);
}
diff --git a/src/pages/workspace/WorkspacePageWithSections.js b/src/pages/workspace/WorkspacePageWithSections.js
index d1acbc2306c4..6eaf7118fca4 100644
--- a/src/pages/workspace/WorkspacePageWithSections.js
+++ b/src/pages/workspace/WorkspacePageWithSections.js
@@ -16,6 +16,7 @@ import * as BankAccounts from '../../libs/actions/BankAccounts';
import BankAccount from '../../libs/models/BankAccount';
import reimbursementAccountPropTypes from '../ReimbursementAccount/reimbursementAccountPropTypes';
import userPropTypes from '../settings/userPropTypes';
+import KeyboardAvoidingView from '../../components/KeyboardAvoidingView';
import withFullPolicy from './withFullPolicy';
import {withNetwork} from '../../components/OnyxProvider';
import networkPropTypes from '../../components/networkPropTypes';
@@ -95,33 +96,35 @@ class WorkspacePageWithSections extends React.Component {
return (
- Navigation.navigate(ROUTES.getWorkspaceInitialRoute(policyID))}
- onCloseButtonPress={() => Navigation.dismissModal()}
- />
- {this.props.reimbursementAccount.loading ? (
-
-
-
- ) : (
- <>
-
-
-
- {this.props.children(hasVBA, policyID, isUsingECard)}
-
-
-
- {this.props.footer}
- >
- )}
+
+ Navigation.navigate(ROUTES.getWorkspaceInitialRoute(policyID))}
+ onCloseButtonPress={() => Navigation.dismissModal()}
+ />
+ {this.props.reimbursementAccount.loading ? (
+
+
+
+ ) : (
+ <>
+
+
+
+ {this.props.children(hasVBA, policyID, isUsingECard)}
+
+
+
+ {this.props.footer}
+ >
+ )}
+
);
}
diff --git a/src/styles/StyleUtils.js b/src/styles/StyleUtils.js
index 7e280bf31429..a34fed355b4b 100644
--- a/src/styles/StyleUtils.js
+++ b/src/styles/StyleUtils.js
@@ -461,6 +461,16 @@ function getPaddingLeft(paddingLeft) {
};
}
+/**
+ * Android only - convert RTL text to a LTR text using Unicode controls.
+ * https://www.w3.org/International/questions/qa-bidi-unicode-controls
+ * @param {String} text
+ * @returns {String}
+ */
+function convertToLTR(text) {
+ return `\u2066${text}`;
+}
+
export {
getAvatarSize,
getAvatarStyle,
@@ -489,4 +499,5 @@ export {
parseStyleAsArray,
combineStyles,
getPaddingLeft,
+ convertToLTR,
};
diff --git a/src/styles/styles.js b/src/styles/styles.js
index f23c94d23526..31efe3cb6be2 100644
--- a/src/styles/styles.js
+++ b/src/styles/styles.js
@@ -15,6 +15,7 @@ import textInputAlignSelf from './utilities/textInputAlignSelf';
import positioning from './utilities/positioning';
import codeStyles from './codeStyles';
import visibility from './utilities/visibility';
+import writingDirection from './utilities/writingDirection';
import optionAlternateTextPlatformStyles from './optionAlternateTextPlatformStyles';
import pointerEventsNone from './pointerEventsNone';
import overflowXHidden from './overflowXHidden';
@@ -148,6 +149,7 @@ const styles = {
...positioning,
...wordBreak,
...whiteSpace,
+ ...writingDirection,
rateCol: {
margin: 0,
@@ -734,6 +736,7 @@ const styles = {
paddingBottom: 8,
paddingHorizontal: 11,
borderWidth: 0,
+ borderRadius: variables.componentBorderRadiusNormal,
},
textInputMultiline: {
diff --git a/src/styles/utilities/writingDirection.js b/src/styles/utilities/writingDirection.js
new file mode 100644
index 000000000000..d9c630c86912
--- /dev/null
+++ b/src/styles/utilities/writingDirection.js
@@ -0,0 +1,13 @@
+/**
+ * Writing direction utility styles.
+ * Note: writingDirection isn't supported on Android. Unicode controls are being used for Android
+ * https://www.w3.org/International/questions/qa-bidi-unicode-controls
+ */
+export default {
+ rtl: {
+ writingDirection: 'rtl',
+ },
+ ltr: {
+ writingDirection: 'ltr',
+ },
+};