Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
versionCode 1001031511
versionName "1.3.15-11"
versionCode 1001031512
versionName "1.3.15-12"
}

splits {
Expand Down
2 changes: 1 addition & 1 deletion ios/NewExpensify/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>1.3.15.11</string>
<string>1.3.15.12</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationQueriesSchemes</key>
Expand Down
2 changes: 1 addition & 1 deletion ios/NewExpensifyTests/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.3.15.11</string>
<string>1.3.15.12</string>
</dict>
</plist>
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
"version": "1.3.15-11",
"version": "1.3.15-12",
"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.",
Expand Down
2 changes: 2 additions & 0 deletions src/CONST.js
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,7 @@ const CONST = {
},
STATE: {
SUBMITTED: 'SUBMITTED',
PROCESSING: 'PROCESSING',
},
STATE_NUM: {
OPEN: 0,
Expand Down Expand Up @@ -960,6 +961,7 @@ const CONST = {
ELSEWHERE: 'Elsewhere',
EXPENSIFY: 'Expensify',
PAYPAL_ME: 'PayPal.me',
VBBA: 'ACH',
},
MONEY_REQUEST_TYPE: {
SEND: 'send',
Expand Down
3 changes: 3 additions & 0 deletions src/ONYXKEYS.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ export default {
// A unique identifier that each user has that's used to send notifications
NVP_PRIVATE_PUSH_NOTIFICATION_ID: 'private_pushNotificationID',

// The NVP with the last payment method used per policy
NVP_LAST_PAYMENT_METHOD: 'nvp_lastPaymentMethod',

// Does this user have push notifications enabled for this device?
PUSH_NOTIFICATIONS_ENABLED: 'pushNotificationsEnabled',

Expand Down
1 change: 1 addition & 0 deletions src/ROUTES.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const SETTINGS_CONTACT_METHODS = 'settings/profile/contact-methods';

export default {
BANK_ACCOUNT: 'bank-account',
BANK_ACCOUNT_NEW: 'bank-account/new',
BANK_ACCOUNT_WITH_STEP_TO_OPEN: 'bank-account/:stepToOpen?',
BANK_ACCOUNT_PERSONAL: 'bank-account/personal',
getBankAccountRoute: (stepToOpen = '', policyID = '') => `bank-account/${stepToOpen}?policyID=${policyID}`,
Expand Down
11 changes: 8 additions & 3 deletions src/components/AddPaymentMethodMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,16 @@ import PopoverMenu from './PopoverMenu';
import paypalMeDataPropTypes from './paypalMeDataPropTypes';

const propTypes = {
/** Should the component be visible? */
isVisible: PropTypes.bool.isRequired,

/** Callback to execute when the component closes. */
onClose: PropTypes.func.isRequired,

/** Anchor position for the AddPaymentMenu. */
anchorPosition: PropTypes.shape({
top: PropTypes.number,
left: PropTypes.number,
horizontal: PropTypes.number,
vertical: PropTypes.number,
}),

/** Account details for PayPal.Me */
Expand All @@ -43,7 +48,7 @@ const AddPaymentMethodMenu = (props) => (
isVisible={props.isVisible}
onClose={props.onClose}
anchorPosition={props.anchorPosition}
onItemSelected={() => props.onClose()}
onItemSelected={props.onClose}
menuItems={[
{
text: props.translate('common.bankAccount'),
Expand Down
1 change: 1 addition & 0 deletions src/components/AvatarWithImagePicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ class AvatarWithImagePicker extends React.Component {
onItemSelected={() => this.setState({isMenuVisible: false})}
menuItems={this.createMenuItems(openPicker)}
anchorPosition={this.props.anchorPosition}
anchorAlignment={this.props.anchorAlignment}
/>
</>
)}
Expand Down
18 changes: 17 additions & 1 deletion src/components/Button/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ const propTypes = {

/** Accessibility label for the component */
accessibilityLabel: PropTypes.string,

/** A ref to forward the button */
forwardedRef: PropTypes.oneOfType([PropTypes.func, PropTypes.shape({current: PropTypes.oneOfType([PropTypes.instanceOf(React.Component), PropTypes.func])})]),
};

const defaultProps = {
Expand Down Expand Up @@ -141,6 +144,7 @@ const defaultProps = {
shouldEnableHapticFeedback: false,
nativeID: '',
accessibilityLabel: '',
forwardedRef: undefined,
};

class Button extends Component {
Expand Down Expand Up @@ -240,6 +244,7 @@ class Button extends Component {
render() {
return (
<PressableWithFeedback
ref={this.props.forwardedRef}
onPress={(e) => {
if (e && e.type === 'click') {
e.currentTarget.blur();
Expand Down Expand Up @@ -303,4 +308,15 @@ class Button extends Component {
Button.propTypes = propTypes;
Button.defaultProps = defaultProps;

export default compose(withNavigationFallback, withNavigationFocus)(Button);
export default compose(
withNavigationFallback,
withNavigationFocus,
)(
React.forwardRef((props, ref) => (
<Button
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
)),
);
65 changes: 0 additions & 65 deletions src/components/ButtonWithDropdown.js

This file was deleted.

139 changes: 139 additions & 0 deletions src/components/ButtonWithDropdownMenu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import React, {useState, useRef, useEffect} from 'react';
import PropTypes from 'prop-types';
import {View} from 'react-native';
import _ from 'underscore';
import useWindowDimensions from '../hooks/useWindowDimensions';
import styles from '../styles/styles';
import Button from './Button';
import PopoverMenu from './PopoverMenu';
import Icon from './Icon';
import * as Expensicons from './Icon/Expensicons';
import themeColors from '../styles/themes/default';
import CONST from '../CONST';

const propTypes = {
/** Text to display for the menu header */
menuHeaderText: PropTypes.string,

/** Callback to execute when the main button is pressed */
onPress: PropTypes.func.isRequired,

/** Whether we should show a loading state for the main button */
isLoading: PropTypes.bool,

/** Should the confirmation button be disabled? */
isDisabled: PropTypes.bool,

/** Additional styles to add to the component */
style: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]),

/** Menu options to display */
/** e.g. [{text: 'Pay with Expensify', icon: Wallet}, {text: 'PayPal', icon: PayPal}] */
options: PropTypes.arrayOf(
PropTypes.shape({
value: PropTypes.string.isRequired,
text: PropTypes.string.isRequired,
icon: PropTypes.elementType,
iconWidth: PropTypes.number,
iconHeight: PropTypes.number,
iconDescription: PropTypes.string,
}),
).isRequired,
};

const defaultProps = {
isLoading: false,
isDisabled: false,
menuHeaderText: '',
style: [],
};

const ButtonWithDropdownMenu = (props) => {
const [selectedItemIndex, setSelectedItemIndex] = useState(0);
const [isMenuVisible, setIsMenuVisible] = useState(false);
const [popoverAnchorPosition, setPopoverAnchorPosition] = useState(null);
const {width: windowWidth, height: windowHeight} = useWindowDimensions();
const caretButton = useRef(null);
useEffect(() => {
if (!caretButton.current) {
return;
}
caretButton.current.measureInWindow((x, y, w, h) => {
setPopoverAnchorPosition({
horizontal: x + w,
vertical: y + h,
});
});
}, [windowWidth, windowHeight]);

const selectedItem = props.options[selectedItemIndex];
return (
<View>
{props.options.length > 1 ? (
<View style={[styles.flexRow, styles.justifyContentBetween, styles.alignItemsCenter, ...props.style]}>
<Button
success
onPress={(event) => props.onPress(event, selectedItem.value)}
text={selectedItem.text}
isDisabled={props.isDisabled}
isLoading={props.isLoading}
shouldRemoveRightBorderRadius
style={[styles.flex1, styles.pr0]}
pressOnEnter
/>
<View style={styles.buttonDivider} />
<Button
ref={caretButton}
success
isDisabled={props.isDisabled}
style={[styles.pl0]}
onPress={() => {
setIsMenuVisible(true);
}}
shouldRemoveLeftBorderRadius
>
<Icon
src={Expensicons.DownArrow}
fill={themeColors.textLight}
/>
</Button>
</View>
) : (
<Button
success
isDisabled={props.isDisabled}
style={[styles.w100, ...props.style]}
isLoading={props.isLoading}
text={selectedItem.text}
onPress={(event) => props.onPress(event, props.options[0].value)}
pressOnEnter
/>
)}
{props.options.length > 1 && !_.isEmpty(popoverAnchorPosition) && (
<PopoverMenu
isVisible={isMenuVisible}
onClose={() => setIsMenuVisible(false)}
onItemSelected={() => setIsMenuVisible(false)}
anchorPosition={popoverAnchorPosition}
anchorAlignment={{
horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT,
vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM,
}}
headerText={props.menuHeaderText}
menuItems={_.map(props.options, (item, index) => ({
...item,
onSelected: () => {
setSelectedItemIndex(index);
},
}))}
/>
)}
</View>
);
};

ButtonWithDropdownMenu.propTypes = propTypes;
ButtonWithDropdownMenu.defaultProps = defaultProps;
ButtonWithDropdownMenu.displayName = 'ButtonWithDropdownMenu';

export default React.memo(ButtonWithDropdownMenu);
Loading