From 5b1b51bfd343a31163f096e858824a819e04e779 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Sun, 11 Jul 2021 11:33:45 +0300 Subject: [PATCH 01/38] make Button platform specific --- src/components/Button/ButtonPropTypes.js | 66 ++++++++++++++++ src/components/{Button.js => Button/index.js} | 70 ++--------------- src/components/Button/index.native.js | 77 +++++++++++++++++++ 3 files changed, 148 insertions(+), 65 deletions(-) create mode 100644 src/components/Button/ButtonPropTypes.js rename src/components/{Button.js => Button/index.js} (58%) create mode 100644 src/components/Button/index.native.js diff --git a/src/components/Button/ButtonPropTypes.js b/src/components/Button/ButtonPropTypes.js new file mode 100644 index 000000000000..8e7eb54b7542 --- /dev/null +++ b/src/components/Button/ButtonPropTypes.js @@ -0,0 +1,66 @@ +import PropTypes from 'prop-types'; + +const propTypes = { + /** The text for the button label */ + text: PropTypes.string, + + /** Small sized button */ + small: PropTypes.bool, + + /** Large sized button */ + large: PropTypes.bool, + + /** Indicates whether the button should be disabled and in the loading state */ + isLoading: PropTypes.bool, + + /** Indicates whether the button should be disabled */ + isDisabled: PropTypes.bool, + + /** A function that is called when the button is clicked on */ + onPress: PropTypes.func, + + /** Additional styles to add after local styles */ + style: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.object), + PropTypes.object, + ]), + + /** Additional text styles */ + textStyles: PropTypes.arrayOf(PropTypes.object), + + /** Whether we should use the success theme color */ + success: PropTypes.bool, + + /** Whether we should use the danger theme color */ + danger: PropTypes.bool, + + /** Optional content component to replace all inner contents of button */ + ContentComponent: PropTypes.func, + + /** Should we remove the right border radius top + bottom? */ + shouldRemoveRightBorderRadius: PropTypes.bool, + + /** Should we remove the left border radius top + bottom? */ + shouldRemoveLeftBorderRadius: PropTypes.bool, +}; + +const defaultProps = { + text: '', + isLoading: false, + isDisabled: false, + small: false, + large: false, + onPress: () => {}, + style: [], + textStyles: [], + success: false, + danger: false, + ContentComponent: undefined, + shouldRemoveRightBorderRadius: false, + shouldRemoveLeftBorderRadius: false, +}; + +export { + propTypes, + defaultProps, +}; diff --git a/src/components/Button.js b/src/components/Button/index.js similarity index 58% rename from src/components/Button.js rename to src/components/Button/index.js index 1b26159706ad..8d7cfdaee712 100644 --- a/src/components/Button.js +++ b/src/components/Button/index.js @@ -1,73 +1,13 @@ import _ from 'underscore'; import React from 'react'; -import PropTypes from 'prop-types'; +import {propTypes, defaultProps} from './ButtonPropTypes'; import { Pressable, ActivityIndicator, } from 'react-native'; -import styles from '../styles/styles'; -import themeColors from '../styles/themes/default'; -import OpacityView from './OpacityView'; -import Text from './Text'; - -const propTypes = { - /** The text for the button label */ - text: PropTypes.string, - - /** Small sized button */ - small: PropTypes.bool, - - /** Large sized button */ - large: PropTypes.bool, - - /** Indicates whether the button should be disabled and in the loading state */ - isLoading: PropTypes.bool, - - /** Indicates whether the button should be disabled */ - isDisabled: PropTypes.bool, - - /** A function that is called when the button is clicked on */ - onPress: PropTypes.func, - - /** Additional styles to add after local styles */ - style: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.object), - PropTypes.object, - ]), - - /** Additional text styles */ - textStyles: PropTypes.arrayOf(PropTypes.object), - - /** Whether we should use the success theme color */ - success: PropTypes.bool, - - /** Whether we should use the danger theme color */ - danger: PropTypes.bool, - - /** Optional content component to replace all inner contents of button */ - ContentComponent: PropTypes.func, - - /** Should we remove the right border radius top + bottom? */ - shouldRemoveRightBorderRadius: PropTypes.bool, - - /** Should we remove the left border radius top + bottom? */ - shouldRemoveLeftBorderRadius: PropTypes.bool, -}; - -const defaultProps = { - text: '', - isLoading: false, - isDisabled: false, - small: false, - large: false, - onPress: () => {}, - style: [], - textStyles: [], - success: false, - danger: false, - ContentComponent: undefined, - shouldRemoveRightBorderRadius: false, - shouldRemoveLeftBorderRadius: false, -}; +import styles from '../../styles/styles'; +import themeColors from '../../styles/themes/default'; +import OpacityView from '../OpacityView'; +import Text from '../Text'; const Button = (props) => { const additionalStyles = _.isArray(props.style) ? props.style : [props.style]; diff --git a/src/components/Button/index.native.js b/src/components/Button/index.native.js new file mode 100644 index 000000000000..8d7cfdaee712 --- /dev/null +++ b/src/components/Button/index.native.js @@ -0,0 +1,77 @@ +import _ from 'underscore'; +import React from 'react'; +import {propTypes, defaultProps} from './ButtonPropTypes'; +import { + Pressable, ActivityIndicator, +} from 'react-native'; +import styles from '../../styles/styles'; +import themeColors from '../../styles/themes/default'; +import OpacityView from '../OpacityView'; +import Text from '../Text'; + +const Button = (props) => { + const additionalStyles = _.isArray(props.style) ? props.style : [props.style]; + + function renderContent() { + const {ContentComponent} = props; + if (ContentComponent) { + return ; + } + + return props.isLoading + ? ( + + ) : ( + + {props.text} + + ); + } + + return ( + + {({pressed, hovered}) => ( + + {renderContent()} + + )} + + ); +}; + +Button.propTypes = propTypes; +Button.defaultProps = defaultProps; +Button.displayName = 'Button'; + +export default Button; From 7abd177ee2c5be21e463f3c08db9a8a66fdb87bc Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Mon, 12 Jul 2021 01:50:57 +0300 Subject: [PATCH 02/38] add pressOnEnter prop --- src/components/Button/ButtonPropTypes.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/Button/ButtonPropTypes.js b/src/components/Button/ButtonPropTypes.js index 8e7eb54b7542..4267a7d64bdd 100644 --- a/src/components/Button/ButtonPropTypes.js +++ b/src/components/Button/ButtonPropTypes.js @@ -19,6 +19,9 @@ const propTypes = { /** A function that is called when the button is clicked on */ onPress: PropTypes.func, + /** Call the onPress function when Enter key is pressed */ + pressOnEnter: PropTypes.bool, + /** Additional styles to add after local styles */ style: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.object), @@ -51,6 +54,7 @@ const defaultProps = { small: false, large: false, onPress: () => {}, + pressOnEnter: false, style: [], textStyles: [], success: false, From 15ec2769aeee312541cd0753987f883c6f070dfa Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Mon, 12 Jul 2021 02:05:13 +0300 Subject: [PATCH 03/38] add eventListener on mount, rm on unmount --- src/components/Button/index.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/Button/index.js b/src/components/Button/index.js index 8d7cfdaee712..8430cb163a74 100644 --- a/src/components/Button/index.js +++ b/src/components/Button/index.js @@ -1,5 +1,5 @@ import _ from 'underscore'; -import React from 'react'; +import React, {useEffect} from 'react'; import {propTypes, defaultProps} from './ButtonPropTypes'; import { Pressable, ActivityIndicator, @@ -12,6 +12,15 @@ import Text from '../Text'; const Button = (props) => { const additionalStyles = _.isArray(props.style) ? props.style : [props.style]; + useEffect(() => { + if (props.pressOnEnter && props.onPress && !props.isDisabled && !props.isLoading) { + document.addEventListener('keydown', handleKeydown); + return () => { + document.removeEventListener('keydown', handleKeydown); + } + } + }, [props.isDisabled, props.isLoading]); + function renderContent() { const {ContentComponent} = props; if (ContentComponent) { From 91ca72dab02ec2919cfa0fcd9265fed70156462f Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Mon, 12 Jul 2021 02:06:54 +0300 Subject: [PATCH 04/38] define handleKeydown() --- src/components/Button/index.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/components/Button/index.js b/src/components/Button/index.js index 8430cb163a74..597e586e8b68 100644 --- a/src/components/Button/index.js +++ b/src/components/Button/index.js @@ -12,6 +12,17 @@ import Text from '../Text'; const Button = (props) => { const additionalStyles = _.isArray(props.style) ? props.style : [props.style]; + /** + * Call props.onPress when Enter key is pressed + * + * @param {Object} event + */ + function handleKeydown(event) { + if (event.key === 'Enter') { + props.onPress(); + } + } + useEffect(() => { if (props.pressOnEnter && props.onPress && !props.isDisabled && !props.isLoading) { document.addEventListener('keydown', handleKeydown); From ecd8a565c3097ee101de7f14ec2e659fa33f798e Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Mon, 12 Jul 2021 02:14:03 +0300 Subject: [PATCH 05/38] press button on enter key --- src/components/IOUConfirmationList.js | 2 ++ src/pages/settings/Payments/AddPayPalMePage.js | 2 ++ src/pages/workspace/NewWorkspacePage.js | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/components/IOUConfirmationList.js b/src/components/IOUConfirmationList.js index 3cec85b899a5..ed39f302011d 100755 --- a/src/components/IOUConfirmationList.js +++ b/src/components/IOUConfirmationList.js @@ -270,6 +270,7 @@ class IOUConfirmationList extends Component { onChangeText={this.props.onUpdateComment} placeholder={this.props.translate('common.optional')} placeholderTextColor={themeColors.placeholderText} + onSubmitEditing={() => this.props.onConfirm(this.getSplits())} /> @@ -280,6 +281,7 @@ class IOUConfirmationList extends Component { isLoading={this.props.iou.loading} text={buttonText} onPress={() => this.props.onConfirm(this.getSplits())} + pressOnEnter /> diff --git a/src/pages/settings/Payments/AddPayPalMePage.js b/src/pages/settings/Payments/AddPayPalMePage.js index e6601581b0bc..774de3dbe51d 100644 --- a/src/pages/settings/Payments/AddPayPalMePage.js +++ b/src/pages/settings/Payments/AddPayPalMePage.js @@ -94,6 +94,7 @@ class AddPayPalMePage extends React.Component { onChangeText={text => this.setState({payPalMeUsername: text})} editable={!this.props.payPalMeUsername} returnKeyType="done" + onSubmitEditing={this.setPayPalMeUsername} /> @@ -102,6 +103,7 @@ class AddPayPalMePage extends React.Component { success isDisabled={Boolean(this.props.payPalMeUsername)} onPress={this.setPayPalMeUsername} + pressOnEnter style={[styles.mt3]} text={this.props.translate('addPayPalMePage.addPayPalAccount')} /> diff --git a/src/pages/workspace/NewWorkspacePage.js b/src/pages/workspace/NewWorkspacePage.js index 8515edea97a6..4868ae8ac011 100644 --- a/src/pages/workspace/NewWorkspacePage.js +++ b/src/pages/workspace/NewWorkspacePage.js @@ -65,6 +65,7 @@ class NewWorkspacePage extends React.Component { label={this.props.translate('workspace.new.chooseAName')} value={this.state.name} onChangeText={name => this.setState({name})} + onSubmitEditting={this.submit} /> {this.props.translate('workspace.new.helpText')} @@ -74,6 +75,7 @@ class NewWorkspacePage extends React.Component { style={[styles.w100]} text={this.props.translate('workspace.new.getStarted')} onPress={this.submit} + pressOnEnter /> From 29546c0865b6aee55c34e9370f0fc510f719ffb8 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Mon, 12 Jul 2021 02:20:47 +0300 Subject: [PATCH 06/38] eslint --- src/components/Button/index.js | 6 +++--- src/components/Button/index.native.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Button/index.js b/src/components/Button/index.js index 597e586e8b68..f6ed0e8f97b4 100644 --- a/src/components/Button/index.js +++ b/src/components/Button/index.js @@ -1,9 +1,9 @@ import _ from 'underscore'; import React, {useEffect} from 'react'; -import {propTypes, defaultProps} from './ButtonPropTypes'; import { Pressable, ActivityIndicator, } from 'react-native'; +import {propTypes, defaultProps} from './ButtonPropTypes'; import styles from '../../styles/styles'; import themeColors from '../../styles/themes/default'; import OpacityView from '../OpacityView'; @@ -17,7 +17,7 @@ const Button = (props) => { * * @param {Object} event */ - function handleKeydown(event) { + function handleKeydown(event) { if (event.key === 'Enter') { props.onPress(); } @@ -28,7 +28,7 @@ const Button = (props) => { document.addEventListener('keydown', handleKeydown); return () => { document.removeEventListener('keydown', handleKeydown); - } + }; } }, [props.isDisabled, props.isLoading]); diff --git a/src/components/Button/index.native.js b/src/components/Button/index.native.js index 8d7cfdaee712..1497c9ea9691 100644 --- a/src/components/Button/index.native.js +++ b/src/components/Button/index.native.js @@ -1,9 +1,9 @@ import _ from 'underscore'; import React from 'react'; -import {propTypes, defaultProps} from './ButtonPropTypes'; import { Pressable, ActivityIndicator, } from 'react-native'; +import {propTypes, defaultProps} from './ButtonPropTypes'; import styles from '../../styles/styles'; import themeColors from '../../styles/themes/default'; import OpacityView from '../OpacityView'; From f62c8e8747442124be64617be16978b52af68276 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Mon, 12 Jul 2021 08:30:34 +0300 Subject: [PATCH 07/38] cleanup iouAmountPage by using pressOnEnter --- src/pages/iou/steps/IOUAmountPage.js | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/pages/iou/steps/IOUAmountPage.js b/src/pages/iou/steps/IOUAmountPage.js index 9cdb9c414abd..437731df258b 100755 --- a/src/pages/iou/steps/IOUAmountPage.js +++ b/src/pages/iou/steps/IOUAmountPage.js @@ -77,31 +77,14 @@ class IOUAmountPage extends React.Component { componentDidMount() { // Component is not initialized yet due to navigation transitions - // Wait until interactions are complete before trying to focus or attach listener + // Wait until interactions are complete before trying to focus InteractionManager.runAfterInteractions(() => { - // Setup and attach keypress handler for navigating to the next screen - this.unsubscribe = KeyboardShortcut.subscribe('Enter', () => { - if (this.state.amount !== '') { - this.props.onStepComplete(this.state.amount); - } - }, [], true); - // Focus text input if (this.textInput) { this.textInput.focus(); } }); } - - componentWillUnmount() { - // Cleanup all keydown event listeners that we've set up - if (!this.unsubscribe) { - return; - } - - this.unsubscribe(); - } - /** * Check if amount is a decimal upto 3 digits * @@ -197,6 +180,7 @@ class IOUAmountPage extends React.Component { success style={[styles.w100, styles.mt5]} onPress={() => this.props.onStepComplete(this.state.amount)} + pressOnEnter isDisabled={!this.state.amount.length || parseFloat(this.state.amount) < 0.01} text={this.props.translate('common.next')} /> From c3ad5fb845523a80b30d303b38176d602517eda4 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Mon, 12 Jul 2021 08:36:50 +0300 Subject: [PATCH 08/38] convert to class comp and use KeyboardShortcut --- src/components/Button/index.js | 130 ++++++++++++++++++--------------- 1 file changed, 71 insertions(+), 59 deletions(-) diff --git a/src/components/Button/index.js b/src/components/Button/index.js index f6ed0e8f97b4..b54e93594d79 100644 --- a/src/components/Button/index.js +++ b/src/components/Button/index.js @@ -1,44 +1,54 @@ import _ from 'underscore'; -import React, {useEffect} from 'react'; +import React, {Component} from 'react'; import { - Pressable, ActivityIndicator, + Pressable, ActivityIndicator, InteractionManager } from 'react-native'; import {propTypes, defaultProps} from './ButtonPropTypes'; import styles from '../../styles/styles'; import themeColors from '../../styles/themes/default'; import OpacityView from '../OpacityView'; import Text from '../Text'; +import KeyboardShortcut from '../../libs/KeyboardShortcut'; -const Button = (props) => { - const additionalStyles = _.isArray(props.style) ? props.style : [props.style]; +class Button extends Component { + constructor(props) { + super(props); + this.additionalStyles = _.isArray(this.props.style) ? this.props.style : [this.props.style]; - /** - * Call props.onPress when Enter key is pressed - * - * @param {Object} event - */ - function handleKeydown(event) { - if (event.key === 'Enter') { - props.onPress(); + this.renderContent = this.renderContent.bind(this); + } + + componentDidMount() { + if (!this.props.pressOnEnter) { + return; } + // Component is not initialized yet due to navigation transitions + // Wait until interactions are complete before trying to attach listener + InteractionManager.runAfterInteractions(() => { + // Setup and attach keypress handler for pressing the button with Enter key + this.unsubscribe = KeyboardShortcut.subscribe('Enter', () => { + if (this.props.pressOnEnter && !this.props.isDisabled && !this.props.isLoading) { + this.props.onPress(); + } + }, [], true); + }); } - useEffect(() => { - if (props.pressOnEnter && props.onPress && !props.isDisabled && !props.isLoading) { - document.addEventListener('keydown', handleKeydown); - return () => { - document.removeEventListener('keydown', handleKeydown); - }; + componentWillUnmount() { + // Cleanup event listeners + if (!this.unsubscribe) { + return; } - }, [props.isDisabled, props.isLoading]); + this.unsubscribe(); + } - function renderContent() { - const {ContentComponent} = props; + renderContent() { + const {ContentComponent} = this.props; if (ContentComponent) { return ; } - return props.isLoading + return this.props.isLoading ? ( ) : ( @@ -46,49 +56,51 @@ const Button = (props) => { selectable={false} style={[ styles.buttonText, - props.small && styles.buttonSmallText, - props.large && styles.buttonLargeText, - props.success && styles.buttonSuccessText, - props.danger && styles.buttonDangerText, - ...props.textStyles, + this.props.small && styles.buttonSmallText, + this.props.large && styles.buttonLargeText, + this.props.success && styles.buttonSuccessText, + this.props.danger && styles.buttonDangerText, + ...this.props.textStyles, ]} > - {props.text} + {this.props.text} ); } - return ( - - {({pressed, hovered}) => ( - - {renderContent()} - - )} - - ); -}; + render() { + return ( + + {({pressed, hovered}) => ( + + {this.renderContent()} + + )} + + ); + } +} Button.propTypes = propTypes; Button.defaultProps = defaultProps; From 8db292449f20f4cc610444ebd34dcf3b4e712a16 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Mon, 12 Jul 2021 08:40:07 +0300 Subject: [PATCH 09/38] rm platform specific code for Button --- src/components/{Button/index.js => Button.js} | 76 ++++++++++++++++-- src/components/Button/ButtonPropTypes.js | 70 ----------------- src/components/Button/index.native.js | 77 ------------------- 3 files changed, 70 insertions(+), 153 deletions(-) rename src/components/{Button/index.js => Button.js} (65%) delete mode 100644 src/components/Button/ButtonPropTypes.js delete mode 100644 src/components/Button/index.native.js diff --git a/src/components/Button/index.js b/src/components/Button.js similarity index 65% rename from src/components/Button/index.js rename to src/components/Button.js index b54e93594d79..a9f62ad0997a 100644 --- a/src/components/Button/index.js +++ b/src/components/Button.js @@ -3,12 +3,76 @@ import React, {Component} from 'react'; import { Pressable, ActivityIndicator, InteractionManager } from 'react-native'; -import {propTypes, defaultProps} from './ButtonPropTypes'; -import styles from '../../styles/styles'; -import themeColors from '../../styles/themes/default'; -import OpacityView from '../OpacityView'; -import Text from '../Text'; -import KeyboardShortcut from '../../libs/KeyboardShortcut'; +import PropTypes from 'prop-types'; +import styles from '../styles/styles'; +import themeColors from '../styles/themes/default'; +import OpacityView from './OpacityView'; +import Text from './Text'; +import KeyboardShortcut from '../libs/KeyboardShortcut'; + +const propTypes = { + /** The text for the button label */ + text: PropTypes.string, + + /** Small sized button */ + small: PropTypes.bool, + + /** Large sized button */ + large: PropTypes.bool, + + /** Indicates whether the button should be disabled and in the loading state */ + isLoading: PropTypes.bool, + + /** Indicates whether the button should be disabled */ + isDisabled: PropTypes.bool, + + /** A function that is called when the button is clicked on */ + onPress: PropTypes.func, + + /** Call the onPress function when Enter key is pressed */ + pressOnEnter: PropTypes.bool, + + /** Additional styles to add after local styles */ + style: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.object), + PropTypes.object, + ]), + + /** Additional text styles */ + textStyles: PropTypes.arrayOf(PropTypes.object), + + /** Whether we should use the success theme color */ + success: PropTypes.bool, + + /** Whether we should use the danger theme color */ + danger: PropTypes.bool, + + /** Optional content component to replace all inner contents of button */ + ContentComponent: PropTypes.func, + + /** Should we remove the right border radius top + bottom? */ + shouldRemoveRightBorderRadius: PropTypes.bool, + + /** Should we remove the left border radius top + bottom? */ + shouldRemoveLeftBorderRadius: PropTypes.bool, +}; + +const defaultProps = { + text: '', + isLoading: false, + isDisabled: false, + small: false, + large: false, + onPress: () => {}, + pressOnEnter: false, + style: [], + textStyles: [], + success: false, + danger: false, + ContentComponent: undefined, + shouldRemoveRightBorderRadius: false, + shouldRemoveLeftBorderRadius: false, +}; class Button extends Component { constructor(props) { diff --git a/src/components/Button/ButtonPropTypes.js b/src/components/Button/ButtonPropTypes.js deleted file mode 100644 index 4267a7d64bdd..000000000000 --- a/src/components/Button/ButtonPropTypes.js +++ /dev/null @@ -1,70 +0,0 @@ -import PropTypes from 'prop-types'; - -const propTypes = { - /** The text for the button label */ - text: PropTypes.string, - - /** Small sized button */ - small: PropTypes.bool, - - /** Large sized button */ - large: PropTypes.bool, - - /** Indicates whether the button should be disabled and in the loading state */ - isLoading: PropTypes.bool, - - /** Indicates whether the button should be disabled */ - isDisabled: PropTypes.bool, - - /** A function that is called when the button is clicked on */ - onPress: PropTypes.func, - - /** Call the onPress function when Enter key is pressed */ - pressOnEnter: PropTypes.bool, - - /** Additional styles to add after local styles */ - style: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.object), - PropTypes.object, - ]), - - /** Additional text styles */ - textStyles: PropTypes.arrayOf(PropTypes.object), - - /** Whether we should use the success theme color */ - success: PropTypes.bool, - - /** Whether we should use the danger theme color */ - danger: PropTypes.bool, - - /** Optional content component to replace all inner contents of button */ - ContentComponent: PropTypes.func, - - /** Should we remove the right border radius top + bottom? */ - shouldRemoveRightBorderRadius: PropTypes.bool, - - /** Should we remove the left border radius top + bottom? */ - shouldRemoveLeftBorderRadius: PropTypes.bool, -}; - -const defaultProps = { - text: '', - isLoading: false, - isDisabled: false, - small: false, - large: false, - onPress: () => {}, - pressOnEnter: false, - style: [], - textStyles: [], - success: false, - danger: false, - ContentComponent: undefined, - shouldRemoveRightBorderRadius: false, - shouldRemoveLeftBorderRadius: false, -}; - -export { - propTypes, - defaultProps, -}; diff --git a/src/components/Button/index.native.js b/src/components/Button/index.native.js deleted file mode 100644 index 1497c9ea9691..000000000000 --- a/src/components/Button/index.native.js +++ /dev/null @@ -1,77 +0,0 @@ -import _ from 'underscore'; -import React from 'react'; -import { - Pressable, ActivityIndicator, -} from 'react-native'; -import {propTypes, defaultProps} from './ButtonPropTypes'; -import styles from '../../styles/styles'; -import themeColors from '../../styles/themes/default'; -import OpacityView from '../OpacityView'; -import Text from '../Text'; - -const Button = (props) => { - const additionalStyles = _.isArray(props.style) ? props.style : [props.style]; - - function renderContent() { - const {ContentComponent} = props; - if (ContentComponent) { - return ; - } - - return props.isLoading - ? ( - - ) : ( - - {props.text} - - ); - } - - return ( - - {({pressed, hovered}) => ( - - {renderContent()} - - )} - - ); -}; - -Button.propTypes = propTypes; -Button.defaultProps = defaultProps; -Button.displayName = 'Button'; - -export default Button; From 7a34be010a4ae7a8661180b697e4962a69443dfd Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Mon, 12 Jul 2021 08:43:40 +0300 Subject: [PATCH 10/38] eslint --- src/components/Button.js | 3 ++- src/pages/iou/steps/IOUAmountPage.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/Button.js b/src/components/Button.js index a9f62ad0997a..34e933d12ac0 100644 --- a/src/components/Button.js +++ b/src/components/Button.js @@ -1,7 +1,7 @@ import _ from 'underscore'; import React, {Component} from 'react'; import { - Pressable, ActivityIndicator, InteractionManager + Pressable, ActivityIndicator, InteractionManager, } from 'react-native'; import PropTypes from 'prop-types'; import styles from '../styles/styles'; @@ -86,6 +86,7 @@ class Button extends Component { if (!this.props.pressOnEnter) { return; } + // Component is not initialized yet due to navigation transitions // Wait until interactions are complete before trying to attach listener InteractionManager.runAfterInteractions(() => { diff --git a/src/pages/iou/steps/IOUAmountPage.js b/src/pages/iou/steps/IOUAmountPage.js index 437731df258b..ff94c9b35998 100755 --- a/src/pages/iou/steps/IOUAmountPage.js +++ b/src/pages/iou/steps/IOUAmountPage.js @@ -17,7 +17,6 @@ import withLocalize, {withLocalizePropTypes} from '../../../components/withLocal import compose from '../../../libs/compose'; import Button from '../../../components/Button'; import Text from '../../../components/Text'; -import KeyboardShortcut from '../../../libs/KeyboardShortcut'; const propTypes = { // Whether or not this IOU has multiple participants @@ -85,6 +84,7 @@ class IOUAmountPage extends React.Component { } }); } + /** * Check if amount is a decimal upto 3 digits * From b94d328a92dad5ea4c468700a600d80f32524299 Mon Sep 17 00:00:00 2001 From: John Schuster Date: Mon, 12 Jul 2021 12:14:24 -0500 Subject: [PATCH 11/38] scrubbing Expensify Cash for New Expensify --- CONTRIBUTING.md | 24 ++++++++++++------------ README.md | 12 ++++++------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3980bc12b581..773b56b62b99 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,13 +1,13 @@ -# Contributing to Expensify.cash -Welcome! Thanks for checking out Expensify.cash and for taking the time to contribute! +# Contributing to Expensify +Welcome! Thanks for checking out Expensify and for taking the time to contribute! ## Getting Started -If you would like to become an Expensify.cash contributor, the first step is to read this document in it's entirety. The second step is to review the README guidelines [here](https://github.com/Expensify/Expensify.cash/blob/main/README.md) for a general overview of the code repository (i.e. how to run the app locally, testing, storage, etc). Please read both documents before asking questions, as it may be covered within the documentation. +If you would like to become an Expensify contributor, the first step is to read this document in its entirety. The second step is to review the README guidelines [here](https://github.com/Expensify/Expensify.cash/blob/main/README.md) for a general overview of the code repository (i.e. how to run the app locally, testing, storage, etc). Please read both documents before asking questions, as it may be covered within the documentation. #### Test Accounts -You can create as many accounts as needed in order to test your changes directly from [expensify.cash](https://expensify.cash/). An initial account can be created when logging in for the first time, and additional accounts can be invited by entering a valid email or phone in the "Find or start a chat" input then tapping the avatar. +You can create as many accounts as needed in order to test your changes directly from [the app](https://expensify.cash/). An initial account can be created when logging in for the first time, and additional accounts can be invited by entering a valid email or phone in the "Find or start a chat" input then tapping the avatar. -**Note**: When testing chat functionality in Expensify Cash please do this between accounts you or your fellow contributors own - **do not test chatting with Concierge**, as this diverts to our customer support team. Thank you. +**Note**: When testing chat functionality in the app please do this between accounts you or your fellow contributors own - **do not test chatting with Concierge**, as this diverts to our customer support team. Thank you. ## Code of Conduct This project and everyone participating in it is governed by the Expensify [Code of Conduct](https://github.com/Expensify/Expensify.cash/blob/main/CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [contributors@expensify.com](mailto:contributors@expensify.com). @@ -24,21 +24,21 @@ If you are hired for an Upwork job and have any job-specific questions, please a If you've found a vulnerability, please email security@expensify.com with the subject `Vulnerability Report` instead of creating an issue. ## Payment for Contributions -We hire and pay external contributors via Upwork.com. If you'd like to be paid for contributing, please create an Upwork account and apply for a job in the [Upwork issue list](https://www.upwork.com/ab/jobs/search/?q=Expensify%20React%20Native&sort=recency&user_location_match=2). Payment for your contributions will be made no less than 7 days after the pull request is merged to allow for regression testing. We hire one contributor for each Upwork job. New Expensify.cash contributors are limited to working on one job at a time, however experienced contributors may work on numerous jobs simultaneously. If you have not received payment after 8 days of the PR being deployed to production, please email contributors@expensify.com referencing the GH issue and your GH handle. +We hire and pay external contributors via Upwork.com. If you'd like to be paid for contributing, please create an Upwork account and apply for a job in the [Upwork issue list](https://www.upwork.com/ab/jobs/search/?q=Expensify%20React%20Native&sort=recency&user_location_match=2). Payment for your contributions will be made no less than 7 days after the pull request is merged to allow for regression testing. We hire one contributor for each Upwork job. New contributors are limited to working on one job at a time, however experienced contributors may work on numerous jobs simultaneously. If you have not received payment after 8 days of the PR being deployed to production, please email contributors@expensify.com referencing the GH issue and your GH handle. -## Finding Expensify.cash Jobs -There are two ways you can find an Expensify.cash job that you can contribute to: +## Finding Jobs +There are two ways you can find a job that you can contribute to: #### Finding a job that Expensify posted -This is the most common scenario for contributors. The Expensify team posts Expensify.cash jobs to the Upwork job list [here](https://www.upwork.com/ab/jobs/search/?q=Expensify%20React%20Native&sort=recency&user_location_match=2). Each job in Upwork has a corresponding GitHub issue, which will include instructions to follow. +This is the most common scenario for contributors. The Expensify team posts New Expensify jobs to the Upwork job list [here](https://www.upwork.com/ab/jobs/search/?q=Expensify%20React%20Native&sort=recency&user_location_match=2). Each job in Upwork has a corresponding GitHub issue, which will include instructions to follow. #### Proposing a job that Expensify hasn’t posted In this scenario, it’s possible that you found a bug or enhancement that we haven’t posted to the [Upwork job list](https://www.upwork.com/ab/jobs/search/?q=Expensify%20React%20Native&sort=recency&user_location_match=2) or [Github repository](https://github.com/Expensify/Expensify.cash/issues?q=is%3Aissue). This is an opportunity to propose a job, and (optionally) a solution. If it's a valid job proposal, we will compensate you for the solution and give an additional bonus of $150 for proactively proposing the job. In this case, please take the following steps: - 1. Check to ensure an issue does not already exist in the Expensify.cash Issue list or Upwork job list. Please use your best judgement to search for similar titles and issue descriptions. + 1. Check to ensure an issue does not already exist in the New Expensify Issue list or Upwork job list. Please use your best judgement to search for similar titles and issue descriptions. 2. If your bug or enhancement matches an existing issue, please feel free to comment on that GitHub issue with your findings if you think it’ll help solve a problem. - 3. If there is no existing issue or Upwork job, create a new GitHub issue in the Expensify.cash repo. + 3. If there is no existing issue or Upwork job, create a new GitHub issue in the app's repo. 4. Make sure to fill out all the required information fields in the issue template. 5. Add the `AutoAssignerTriage` label to your issue. 6. Before starting your PR to solve the bug or enhancement that you are proposing, please add a comment on your issue with a solution proposal. @@ -50,7 +50,7 @@ In this scenario, it’s possible that you found a bug or enhancement that we ha > >**Solution:** Start up time will perceptibly decrease by 1042ms if we prevent the unnecessary re-renders of this component. -## Working on Expensify.cash Jobs +## Working on Expensify Jobs *Reminder: For technical guidance please refer to the [README](https://github.com/Expensify/Expensify.cash/blob/main/README.md)*. #### Express interest for the job on Upwork.com diff --git a/README.md b/README.md index d68388ffafbe..da5d751538a8 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ @@ -19,14 +19,14 @@ * [Deploying](#deploying) #### Additional Reading -* [Contributing to Expensify.cash](./CONTRIBUTING.md) +* [Contributing to Expensify](./CONTRIBUTING.md) * [Expensify Code of Conduct](./CODE_OF_CONDUCT.md) * [Contributor License Agreement](./CLA.md) ---- # Local development -These instructions should get you set up ready to work on Expensify.cash 🙌 +These instructions should get you set up ready to work on New Expensify 🙌 ## Getting Started 1. Install `node` & `npm`: `brew install node` From db67656c8ccc252f0d1213b96feb93bb59e7a60c Mon Sep 17 00:00:00 2001 From: Rory Abraham Date: Mon, 12 Jul 2021 15:24:46 -0700 Subject: [PATCH 12/38] Give disabled buttons the disabled cursor style --- src/components/Button.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Button.js b/src/components/Button.js index 1b26159706ad..8a07e157155a 100644 --- a/src/components/Button.js +++ b/src/components/Button.js @@ -103,6 +103,7 @@ const Button = (props) => { onPress={props.onPress} disabled={props.isLoading || props.isDisabled} style={[ + props.isDisabled ? styles.cursorDisabled : {}, ...additionalStyles, ]} > From 9d18b6814f5bd9debf46e300662137b32230e141 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Tue, 13 Jul 2021 12:38:18 +0300 Subject: [PATCH 13/38] rm unnecessary interaction manager --- src/components/Button.js | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/components/Button.js b/src/components/Button.js index 34e933d12ac0..dd77eaaf5913 100644 --- a/src/components/Button.js +++ b/src/components/Button.js @@ -1,8 +1,6 @@ import _ from 'underscore'; import React, {Component} from 'react'; -import { - Pressable, ActivityIndicator, InteractionManager, -} from 'react-native'; +import {Pressable, ActivityIndicator} from 'react-native'; import PropTypes from 'prop-types'; import styles from '../styles/styles'; import themeColors from '../styles/themes/default'; @@ -87,16 +85,12 @@ class Button extends Component { return; } - // Component is not initialized yet due to navigation transitions - // Wait until interactions are complete before trying to attach listener - InteractionManager.runAfterInteractions(() => { - // Setup and attach keypress handler for pressing the button with Enter key - this.unsubscribe = KeyboardShortcut.subscribe('Enter', () => { - if (this.props.pressOnEnter && !this.props.isDisabled && !this.props.isLoading) { - this.props.onPress(); - } - }, [], true); - }); + // Setup and attach keypress handler for pressing the button with Enter key + this.unsubscribe = KeyboardShortcut.subscribe('Enter', () => { + if (!this.props.isDisabled && !this.props.isLoading) { + this.props.onPress(); + } + }, [], true); } componentWillUnmount() { From 72fd1b6bc3217e6f3f1f632413b4efe27ced4ada Mon Sep 17 00:00:00 2001 From: John Schuster Date: Tue, 13 Jul 2021 08:53:08 -0500 Subject: [PATCH 14/38] updates from Tom's feedback --- CONTRIBUTING.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 773b56b62b99..947cfba1851d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,11 +1,11 @@ # Contributing to Expensify -Welcome! Thanks for checking out Expensify and for taking the time to contribute! +Welcome! Thanks for checking out the new Expensify app and for taking the time to contribute! ## Getting Started If you would like to become an Expensify contributor, the first step is to read this document in its entirety. The second step is to review the README guidelines [here](https://github.com/Expensify/Expensify.cash/blob/main/README.md) for a general overview of the code repository (i.e. how to run the app locally, testing, storage, etc). Please read both documents before asking questions, as it may be covered within the documentation. #### Test Accounts -You can create as many accounts as needed in order to test your changes directly from [the app](https://expensify.cash/). An initial account can be created when logging in for the first time, and additional accounts can be invited by entering a valid email or phone in the "Find or start a chat" input then tapping the avatar. +You can create as many accounts as needed in order to test your changes directly from [the app](https://new.expensify.com/). An initial account can be created when logging in for the first time, and additional accounts can be invited by entering a valid email or phone in the "Find or start a chat" input then tapping the avatar. **Note**: When testing chat functionality in the app please do this between accounts you or your fellow contributors own - **do not test chatting with Concierge**, as this diverts to our customer support team. Thank you. @@ -30,7 +30,7 @@ We hire and pay external contributors via Upwork.com. If you'd like to be paid f There are two ways you can find a job that you can contribute to: #### Finding a job that Expensify posted -This is the most common scenario for contributors. The Expensify team posts New Expensify jobs to the Upwork job list [here](https://www.upwork.com/ab/jobs/search/?q=Expensify%20React%20Native&sort=recency&user_location_match=2). Each job in Upwork has a corresponding GitHub issue, which will include instructions to follow. +This is the most common scenario for contributors. The Expensify team posts new jobs to the Upwork job list [here](https://www.upwork.com/ab/jobs/search/?q=Expensify%20React%20Native&sort=recency&user_location_match=2). Each job in Upwork has a corresponding GitHub issue, which will include instructions to follow. #### Proposing a job that Expensify hasn’t posted @@ -38,7 +38,7 @@ In this scenario, it’s possible that you found a bug or enhancement that we ha 1. Check to ensure an issue does not already exist in the New Expensify Issue list or Upwork job list. Please use your best judgement to search for similar titles and issue descriptions. 2. If your bug or enhancement matches an existing issue, please feel free to comment on that GitHub issue with your findings if you think it’ll help solve a problem. - 3. If there is no existing issue or Upwork job, create a new GitHub issue in the app's repo. + 3. If there is no existing issue or Upwork job, create a new GitHub issue in the Expensify/App repo. 4. Make sure to fill out all the required information fields in the issue template. 5. Add the `AutoAssignerTriage` label to your issue. 6. Before starting your PR to solve the bug or enhancement that you are proposing, please add a comment on your issue with a solution proposal. From 7f078a8462758f956816077fa5820f3721ca40c8 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Wed, 14 Jul 2021 03:25:38 +0300 Subject: [PATCH 15/38] remove redundant event listeners from BaseModal --- src/components/Modal/BaseModal.js | 30 +++++------------------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/src/components/Modal/BaseModal.js b/src/components/Modal/BaseModal.js index bfe3da6fe6f8..38d0d580e011 100644 --- a/src/components/Modal/BaseModal.js +++ b/src/components/Modal/BaseModal.js @@ -3,8 +3,6 @@ import {View} from 'react-native'; import PropTypes from 'prop-types'; import ReactNativeModal from 'react-native-modal'; import {SafeAreaInsetsContext} from 'react-native-safe-area-context'; - -import KeyboardShortcut from '../../libs/KeyboardShortcut'; import styles, {getModalPaddingStyles, getSafeAreaPadding} from '../../styles/styles'; import themeColors from '../../styles/themes/default'; import {propTypes as modalPropTypes, defaultProps as modalDefaultProps} from './ModalPropTypes'; @@ -27,22 +25,19 @@ class BaseModal extends PureComponent { constructor(props) { super(props); - this.hideModalAndRemoveEventListeners = this.hideModalAndRemoveEventListeners.bind(this); - this.subscribeToKeyEvents = this.subscribeToKeyEvents.bind(this); - this.unsubscribeFromKeyEvents = this.unsubscribeFromKeyEvents.bind(this); + this.hideModal = this.hideModal.bind(this); } componentWillUnmount() { // we don't want to call the onModalHide on unmount - this.hideModalAndRemoveEventListeners(this.props.isVisible); + this.hideModal(this.props.isVisible); } /** - * Hides modal and unsubscribes from key event listeners + * Hides modal * @param {Boolean} [callHideCallback=true] Should we call the onModalHide callback */ - hideModalAndRemoveEventListeners(callHideCallback = true) { - this.unsubscribeFromKeyEvents(); + hideModal(callHideCallback = true) { if (this.props.shouldSetModalVisibility) { setModalVisibility(false); } @@ -51,20 +46,6 @@ class BaseModal extends PureComponent { } } - /** - * Listens to specific keyboard keys when the modal has been opened - */ - subscribeToKeyEvents() { - KeyboardShortcut.subscribe('Enter', this.props.onSubmit, [], true); - } - - /** - * Stops listening to keyboard keys when modal has been closed - */ - unsubscribeFromKeyEvents() { - KeyboardShortcut.unsubscribe('Enter'); - } - render() { const { modalStyle, @@ -97,14 +78,13 @@ class BaseModal extends PureComponent { // eslint-disable-next-line react/jsx-props-no-multi-spaces onBackButtonPress={this.props.onClose} onModalShow={() => { - this.subscribeToKeyEvents(); if (this.props.shouldSetModalVisibility) { setModalVisibility(true); } this.props.onModalShow(); }} propagateSwipe={this.props.propagateSwipe} - onModalHide={this.hideModalAndRemoveEventListeners} + onModalHide={this.hideModal} onSwipeComplete={this.props.onClose} swipeDirection={swipeDirection} isVisible={this.props.isVisible} From beaed87aaafc097deb5830c0e010fe3b230c17d8 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Wed, 14 Jul 2021 03:35:04 +0300 Subject: [PATCH 16/38] add pressOnEnter to replicate prev behaviour of ConfirmModal --- src/components/ConfirmModal.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/ConfirmModal.js b/src/components/ConfirmModal.js index c7529f974c86..8ddd05768696 100755 --- a/src/components/ConfirmModal.js +++ b/src/components/ConfirmModal.js @@ -71,6 +71,7 @@ const ConfirmModal = props => ( danger={props.danger} style={[styles.mt4]} onPress={props.onConfirm} + pressOnEnter text={props.confirmText || props.translate('common.yes')} />