Skip to content

Commit 4786f07

Browse files
EtDurickycodes
andauthored
Unify Custom Gas Components (#1860)
* Remove redundant TransactionEdit component, move logic to TransactionEditor * add AnimatedCustomGas * Move all animation logic into Animated Transaction Modal * TransactionReviewFeeCard: change toggleCustomGasModal to edit * toggleCustomGasModal --> edit * AnimatedTransactionModal: Make animations function from wallet send flow, custom gas modal renders properly * Confirm: use CustomGas from UI, visually in working state but not yet functionally * Confirm: setting custom gas from selectors or input works * Bug fix: can't tap edit before gas estimation fully complete * bug fix: gas not showing after selection * Confirm: rafactor, move class functions * remove redundant function * ApproveTransactionReview: new components. Takes logic from ApproveView/Approve. Remove dead code * Approve: break out logic into ApproveTransactionReview, plug with AnimatedTransactionModal, use UI/CustomGas, remove dead code * ApprveTransactionReview: add componentDidUpdate to update gas * formatting * ApproveTransactionView: use ActionView for buttons * ESLINT: remove dumb componentDidUpdate no setState rule * Fix paddings * change confirm to approve * add new test + update snapshots * REMOVE SendFlow/CustomGas RIP * use component variables for animated values instead of state * use optional chaining on react children * Customas: remove '' as possible value to checksummedFrom * AnimatedTransactionModal: dedupe code, move all logic into render * Apply changes from #1847 inside ApproveTransactionReview * update snapshot * rafactor gas speed selection, put in parent state (Confirm) to pass down as props * Only apply buttonStyle animation for iOS * Leave space for the gas error * Add margin to the bottom as well * Add no-did-update-set-state rule back and disable where we're doing it instead Co-authored-by: rickycodes <[email protected]>
1 parent 9306c18 commit 4786f07

File tree

23 files changed

+1475
-2196
lines changed

23 files changed

+1475
-2196
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`AnimatedTransactionModal should render correctly 1`] = `
4+
<ForwardRef(AnimatedComponentWrapper)
5+
onLayout={[Function]}
6+
style={
7+
Array [
8+
Object {
9+
"backgroundColor": "#FFFFFF",
10+
"borderTopLeftRadius": 20,
11+
"borderTopRightRadius": 20,
12+
"minHeight": 200,
13+
"paddingBottom": 24,
14+
},
15+
Object {
16+
"transform": Array [
17+
Object {
18+
"translateY": 70,
19+
},
20+
],
21+
},
22+
Object {
23+
"height": 70,
24+
},
25+
]
26+
}
27+
/>
28+
`;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
import React, { PureComponent } from 'react';
2+
import PropTypes from 'prop-types';
3+
import { StyleSheet, Animated, Easing } from 'react-native';
4+
import { colors } from '../../../styles/common';
5+
import Device from '../../../util/Device';
6+
7+
const styles = StyleSheet.create({
8+
root: {
9+
backgroundColor: colors.white,
10+
minHeight: 200,
11+
borderTopLeftRadius: 20,
12+
borderTopRightRadius: 20,
13+
paddingBottom: Device.isIphoneX() ? 24 : 0
14+
},
15+
transactionEdit: {
16+
position: 'absolute',
17+
width: '100%',
18+
height: '100%'
19+
},
20+
transactionReview: {
21+
paddingTop: 24
22+
}
23+
});
24+
25+
/**
26+
* PureComponent that handles most of the animation/transition logic
27+
*/
28+
class AnimatedTransactionModal extends PureComponent {
29+
static propTypes = {
30+
/**
31+
* Changes the mode to 'review'
32+
*/
33+
review: PropTypes.func,
34+
/**
35+
* Called when a user changes modes
36+
*/
37+
onModeChange: PropTypes.func,
38+
/**
39+
* Whether or not basic gas estimates have been fetched
40+
*/
41+
ready: PropTypes.bool,
42+
/**
43+
* Children components
44+
*/
45+
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired
46+
};
47+
48+
state = {
49+
originComponent: React.Children.toArray(this.props?.children).length > 1 ? 'dapp' : 'wallet',
50+
modalValue:
51+
React.Children.toArray(this.props?.children).length > 1 ? new Animated.Value(1) : new Animated.Value(0),
52+
width: Device.getDeviceWidth(),
53+
rootHeight: null,
54+
customGasHeight: null,
55+
transactionReviewDataHeight: null,
56+
hideGasSelectors: false,
57+
hideData: true,
58+
advancedCustomGas: false,
59+
toAdvancedFrom: 'edit',
60+
mode: 'review'
61+
};
62+
63+
reviewToEditValue = new Animated.Value(0);
64+
reviewToDataValue = new Animated.Value(0);
65+
editToAdvancedValue = new Animated.Value(0);
66+
67+
xTranslationMappings = {
68+
reviewToEdit: this.reviewToEditValue,
69+
editToAdvanced: this.editToAdvancedValue,
70+
reviewToData: this.reviewToDataValue
71+
};
72+
73+
review = () => {
74+
this.props.review();
75+
this.onModeChange('review');
76+
};
77+
78+
onModeChange = mode => {
79+
if (mode === 'edit') {
80+
this.setState({ toAdvancedFrom: 'review' });
81+
this.animate({
82+
modalEndValue: this.state.advancedCustomGas ? this.getAnimatedModalValueForAdvancedCG() : 0,
83+
xTranslationName: 'reviewToEdit',
84+
xTranslationEndValue: 1
85+
});
86+
} else {
87+
this.animate({
88+
modalEndValue: 1,
89+
xTranslationName: 'reviewToEdit',
90+
xTranslationEndValue: 0
91+
});
92+
}
93+
this.props.onModeChange(mode);
94+
};
95+
96+
animate = ({ modalEndValue, xTranslationName, xTranslationEndValue }) => {
97+
const { modalValue } = this.state;
98+
this.hideComponents(xTranslationName, xTranslationEndValue, 'start');
99+
Animated.parallel([
100+
Animated.timing(modalValue, {
101+
toValue: modalEndValue,
102+
duration: 250,
103+
easing: Easing.ease,
104+
useNativeDriver: true
105+
}),
106+
Animated.timing(this.xTranslationMappings[xTranslationName], {
107+
toValue: xTranslationEndValue,
108+
duration: 250,
109+
easing: Easing.ease,
110+
useNativeDriver: true
111+
})
112+
]).start(() => {
113+
this.hideComponents(xTranslationName, xTranslationEndValue, 'end');
114+
});
115+
};
116+
117+
toggleAdvancedCustomGas = (toggle = false) => {
118+
const { advancedCustomGas } = this.state;
119+
this.setState({ advancedCustomGas: toggle ? true : !advancedCustomGas, toAdvancedFrom: 'edit' });
120+
};
121+
122+
hideComponents = (xTranslationName, xTranslationEndValue, animationTime) => {
123+
//data view is hidden by default because when we switch from review to edit, since view is nested in review, it also gets transformed. It's shown if it's the animation's destination.
124+
if (xTranslationName === 'editToAdvanced') {
125+
this.setState({
126+
hideGasSelectors: xTranslationEndValue === 1 && animationTime === 'end'
127+
});
128+
}
129+
if (xTranslationName === 'reviewToData') {
130+
this.setState({
131+
hideData: xTranslationEndValue === 0 && animationTime === 'end'
132+
});
133+
}
134+
};
135+
136+
generateTransform = (valueType, outRange) => {
137+
const { modalValue } = this.state;
138+
if (valueType === 'modal' || valueType === 'saveButton') {
139+
return {
140+
transform: [
141+
{
142+
translateY: modalValue.interpolate({
143+
inputRange: [0, valueType === 'saveButton' ? this.getAnimatedModalValueForAdvancedCG() : 1],
144+
outputRange: outRange
145+
})
146+
}
147+
]
148+
};
149+
}
150+
let value;
151+
if (valueType === 'reviewToEdit') value = this.reviewToEditValue;
152+
else if (valueType === 'editToAdvanced') value = this.editToAdvancedValue;
153+
else if (valueType === 'reviewToData') value = this.reviewToDataValue;
154+
return {
155+
transform: [
156+
{
157+
translateX: value.interpolate({
158+
inputRange: [0, 1],
159+
outputRange: outRange
160+
})
161+
}
162+
]
163+
};
164+
};
165+
166+
getAnimatedModalValueForAdvancedCG = () => {
167+
const { rootHeight, customGasHeight, originComponent } = this.state;
168+
if (originComponent === 'wallet') return 1;
169+
//70 is the fixed height + margin of the error message in advanced custom gas. It expands 70 units vertically to accomodate it
170+
return 70 / (rootHeight - customGasHeight);
171+
};
172+
173+
saveRootHeight = event => this.setState({ rootHeight: event.nativeEvent.layout.height });
174+
175+
saveCustomGasHeight = event => this.setState({ customGasHeight: event.nativeEvent.layout.height });
176+
177+
saveTransactionReviewDataHeight = event =>
178+
!this.state.transactionReviewDataHeight &&
179+
this.setState({ transactionReviewDataHeight: event.nativeEvent.layout.height });
180+
181+
getTransformValue = () => {
182+
const { rootHeight, customGasHeight } = this.state;
183+
return rootHeight - customGasHeight;
184+
};
185+
186+
render = () => {
187+
const {
188+
width,
189+
hideData,
190+
originComponent,
191+
customGasHeight,
192+
advancedCustomGas,
193+
hideGasSelectors,
194+
toAdvancedFrom
195+
} = this.state;
196+
const { ready, children } = this.props;
197+
const components = React.Children.toArray(children);
198+
let gasTransformStyle;
199+
let modalTransformStyle;
200+
let gasComponent;
201+
if (originComponent === 'dapp') {
202+
gasTransformStyle = this.generateTransform('reviewToEdit', [width, 0]);
203+
modalTransformStyle = this.generateTransform('modal', [this.getTransformValue(), 0]);
204+
gasComponent = components[1];
205+
} else {
206+
gasTransformStyle = this.generateTransform('reviewToEdit', [0, -width]);
207+
modalTransformStyle = this.generateTransform('modal', [70, 0]);
208+
gasComponent = components[0];
209+
}
210+
211+
return (
212+
<Animated.View
213+
style={[
214+
styles.root,
215+
modalTransformStyle,
216+
originComponent === 'wallet' && { height: customGasHeight + 70 }
217+
]}
218+
onLayout={this.saveRootHeight}
219+
>
220+
{originComponent === 'dapp' && (
221+
<Animated.View
222+
style={[this.generateTransform('reviewToEdit', [0, -width]), styles.transactionReview]}
223+
>
224+
{React.cloneElement(components[0], {
225+
...components[0].props,
226+
customGasHeight,
227+
hideData,
228+
generateTransform: this.generateTransform,
229+
animate: this.animate,
230+
saveTransactionReviewDataHeight: this.saveTransactionReviewDataHeight,
231+
onModeChange: this.onModeChange
232+
})}
233+
</Animated.View>
234+
)}
235+
236+
{ready && (
237+
<Animated.View style={[styles.transactionEdit, gasTransformStyle]}>
238+
{React.cloneElement(gasComponent, {
239+
...gasComponent.props,
240+
advancedCustomGas,
241+
hideGasSelectors,
242+
toAdvancedFrom,
243+
onModeChange: this.onModeChange,
244+
toggleAdvancedCustomGas: this.toggleAdvancedCustomGas,
245+
saveCustomGasHeight: this.saveCustomGasHeight,
246+
animate: this.animate,
247+
generateTransform: this.generateTransform,
248+
getAnimatedModalValueForAdvancedCG: this.getAnimatedModalValueForAdvancedCG,
249+
review: this.review
250+
})}
251+
</Animated.View>
252+
)}
253+
</Animated.View>
254+
);
255+
};
256+
}
257+
258+
export default AnimatedTransactionModal;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import React from 'react';
2+
import { shallow } from 'enzyme';
3+
import AnimatedTransactionModal from './';
4+
5+
describe('AnimatedTransactionModal', () => {
6+
it('should render correctly', () => {
7+
const wrapper = shallow(<AnimatedTransactionModal />);
8+
expect(wrapper).toMatchSnapshot();
9+
});
10+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`ApproveTransactionModal should render correctly 1`] = `
4+
<ApproveTransactionReview
5+
accounts={
6+
Object {
7+
"0x2": Object {
8+
"balance": "0",
9+
},
10+
}
11+
}
12+
accountsLength={1}
13+
activeTabUrl=""
14+
conversionRate={5}
15+
primaryCurrency="fiat"
16+
providerType="ETH"
17+
setTransactionObject={[Function]}
18+
showAlert={[Function]}
19+
ticker="ETH"
20+
tokensLength={0}
21+
transaction={Object {}}
22+
/>
23+
`;

0 commit comments

Comments
 (0)