Skip to content

Commit

Permalink
Merge pull request #4070 from rdjuric/IOUFlowAnimations
Browse files Browse the repository at this point in the history
Animations for the IOU flow
  • Loading branch information
Julesssss authored Nov 17, 2021
2 parents e60aa8c + a712c94 commit 96ba12d
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 31 deletions.
2 changes: 2 additions & 0 deletions src/CONST.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ const CLOUDFRONT_URL = 'https://d2k5nsl2zxldvw.cloudfront.net';
const NEW_EXPENSIFY_URL = 'https://new.expensify.com';

const CONST = {
ANIMATED_TRANSITION: 300,

// 50 megabytes in bytes
API_MAX_ATTACHMENT_SIZE: 52428800,
AVATAR_MAX_ATTACHMENT_SIZE: 3145728,
Expand Down
49 changes: 49 additions & 0 deletions src/components/AnimatedStep.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react';
import PropTypes from 'prop-types';
import * as Animatable from 'react-native-animatable';
import CONST from '../CONST';

const propTypes = {
/** Children to wrap in AnimatedStep. */
children: PropTypes.node.isRequired,

/** Styles to be assigned to Container */
style: PropTypes.arrayOf(PropTypes.object),

/** Whether we're animating the step in or out */
direction: PropTypes.string,
};

const defaultProps = {
direction: 'in',
style: [],
};

const AnimatedStep = (props) => {
function getAnimationStyle(direction) {
let animationStyle;

if (direction === 'in') {
animationStyle = 'slideInRight';
} else if (direction === 'out') {
animationStyle = 'slideInLeft';
}
return animationStyle;
}

return (
<Animatable.View
duration={CONST.ANIMATED_TRANSITION}
animation={getAnimationStyle(props.direction)}
useNativeDriver
style={[...props.style]}
>
{props.children}
</Animatable.View>
);
};

AnimatedStep.propTypes = propTypes;
AnimatedStep.defaultProps = defaultProps;
AnimatedStep.displayName = 'AnimatedStep';
export default AnimatedStep;
6 changes: 5 additions & 1 deletion src/components/IOUConfirmationList.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@ class IOUConfirmationList extends Component {
}

componentDidMount() {
// We need to wait for the transition animation to end before focusing the TextInput,
// otherwise the TextInput isn't animated correctly
setTimeout(() => this.textInput.focus(), CONST.ANIMATED_TRANSITION);

// Only add the Venmo option if we're sending a payment
if (this.props.iouType !== CONST.IOU.IOU_TYPE.SEND) {
return;
Expand Down Expand Up @@ -423,12 +427,12 @@ class IOUConfirmationList extends Component {
</ScrollView>
<View style={[styles.ph5, styles.pv5, styles.flexGrow1, styles.flexShrink0, styles.iouConfirmComment]}>
<ExpensiTextInput
ref={el => this.textInput = el}
label={this.props.translate('iOUConfirmationList.whatsItFor')}
value={this.props.comment}
onChangeText={this.props.onUpdateComment}
placeholder={this.props.translate('common.optional')}
placeholderTextColor={themeColors.placeholderText}
autoFocus
/>
</View>
<FixedFooter>
Expand Down
11 changes: 10 additions & 1 deletion src/components/OptionsSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {View} from 'react-native';
import OptionsList from './OptionsList';
import CONST from '../CONST';
import styles from '../styles/styles';
import optionPropTypes from './optionPropTypes';
import withLocalize, {withLocalizePropTypes} from './withLocalize';
import ExpensiTextInput from './ExpensiTextInput';

const propTypes = {
/** Wether we should wait before focusing the TextInput, useful when using transitions */
shouldDelayFocus: PropTypes.bool,

/** Callback to fire when a row is tapped */
onSelectRow: PropTypes.func,

Expand Down Expand Up @@ -67,6 +71,7 @@ const propTypes = {
};

const defaultProps = {
shouldDelayFocus: false,
onSelectRow: () => {},
placeholderText: '',
selectedOptions: [],
Expand Down Expand Up @@ -94,7 +99,11 @@ class OptionsSelector extends Component {
}

componentDidMount() {
this.textInput.focus();
if (this.props.shouldDelayFocus) {
setTimeout(() => this.textInput.focus(), CONST.ANIMATED_TRANSITION);
} else {
this.textInput.focus();
}
}

/**
Expand Down
91 changes: 64 additions & 27 deletions src/pages/iou/IOUModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize
import compose from '../../libs/compose';
import {addSMSDomainIfPhoneNumber, getPersonalDetailsForLogins} from '../../libs/OptionsListUtils';
import FullScreenLoadingIndicator from '../../components/FullscreenLoadingIndicator';
import AnimatedStep from '../../components/AnimatedStep';
import ScreenWrapper from '../../components/ScreenWrapper';
import Tooltip from '../../components/Tooltip';
import CONST from '../../CONST';
Expand Down Expand Up @@ -126,6 +127,7 @@ class IOUModal extends Component {
this.hasGoldWallet = props.userWallet.tierName && props.userWallet.tiername === CONST.WALLET.TIER_NAME.GOLD;

this.state = {
previousStepIndex: 0,
currentStepIndex: 0,
participants: participantsWithDetails,

Expand Down Expand Up @@ -167,6 +169,25 @@ 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';
}
if (this.state.previousStepIndex > this.state.currentStepIndex) {
return 'out';
}

// Doesn't animate the step when first opening the modal
if (this.state.previousStepIndex === this.state.currentStepIndex) {
return null;
}
}

/**
* Retrieve title for current step, based upon current step and type of IOU
*
Expand Down Expand Up @@ -216,6 +237,7 @@ class IOUModal extends Component {
return;
}
this.setState(prevState => ({
previousStepIndex: prevState.currentStepIndex,
currentStepIndex: prevState.currentStepIndex - 1,
}));
}
Expand All @@ -228,6 +250,7 @@ class IOUModal extends Component {
return;
}
this.setState(prevState => ({
previousStepIndex: prevState.currentStepIndex,
currentStepIndex: prevState.currentStepIndex + 1,
}));
}
Expand Down Expand Up @@ -341,37 +364,51 @@ class IOUModal extends Component {
{didScreenTransitionEnd && (
<>
{currentStep === Steps.IOUAmount && (
<IOUAmountPage
onStepComplete={(amount) => {
this.setState({amount});
this.navigateToNextStep();
}}
reportID={reportID}
hasMultipleParticipants={this.props.hasMultipleParticipants}
selectedAmount={this.state.amount}
navigation={this.props.navigation}
/>
<AnimatedStep
direction={this.getDirection()}
style={[styles.flex1, styles.pageWrapper]}
>
<IOUAmountPage
onStepComplete={(amount) => {
this.setState({amount});
this.navigateToNextStep();
}}
reportID={reportID}
hasMultipleParticipants={this.props.hasMultipleParticipants}
selectedAmount={this.state.amount}
navigation={this.props.navigation}
/>
</AnimatedStep>
)}
{currentStep === Steps.IOUParticipants && (
<IOUParticipantsPage
participants={this.state.participants}
hasMultipleParticipants={this.props.hasMultipleParticipants}
onAddParticipants={this.addParticipants}
onStepComplete={this.navigateToNextStep}
/>
<AnimatedStep
style={[styles.flex1]}
direction={this.getDirection()}
>
<IOUParticipantsPage
participants={this.state.participants}
hasMultipleParticipants={this.props.hasMultipleParticipants}
onAddParticipants={this.addParticipants}
onStepComplete={this.navigateToNextStep}
/>
</AnimatedStep>
)}
{currentStep === Steps.IOUConfirm && (
<IOUConfirmPage
onConfirm={this.createTransaction}
hasMultipleParticipants={this.props.hasMultipleParticipants}
participants={this.state.participants}
iouAmount={this.state.amount}
comment={this.state.comment}
onUpdateComment={this.updateComment}
iouType={this.props.iouType}
localCurrencyCode={this.props.myPersonalDetails.localCurrencyCode}
isGroupSplit={this.steps.length === 2}
/>
<AnimatedStep
direction={this.getDirection()}
>
<IOUConfirmPage
onConfirm={this.createTransaction}
hasMultipleParticipants={this.props.hasMultipleParticipants}
participants={this.state.participants}
iouAmount={this.state.amount}
comment={this.state.comment}
onUpdateComment={this.updateComment}
iouType={this.props.iouType}
localCurrencyCode={this.props.myPersonalDetails.localCurrencyCode}
isGroupSplit={this.steps.length === 2}
/>
</AnimatedStep>
)}
</>
)}
Expand Down
4 changes: 2 additions & 2 deletions src/pages/iou/steps/IOUAmountPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ class IOUAmountPage extends React.Component {

render() {
return (
<View style={[styles.flex1, styles.pageWrapper]}>
<>
<View style={[
styles.flex1,
styles.flexRow,
Expand Down Expand Up @@ -223,7 +223,7 @@ class IOUAmountPage extends React.Component {
text={this.props.translate('common.next')}
/>
</View>
</View>
</>
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ class IOUParticipantsRequest extends Component {
disableArrowKeysActions
hideAdditionalOptionStates
forceTextUnreadStyle
shouldDelayFocus
/>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ class IOUParticipantsSplit extends Component {
disableArrowKeysActions
hideAdditionalOptionStates
forceTextUnreadStyle
shouldDelayFocus
/>
</View>
{lodashGet(this.props, 'participants', []).length > 0 && (
Expand Down

0 comments on commit 96ba12d

Please sign in to comment.