-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Animate settlement button when pay and trigger a haptic feedback #48615
Changes from 11 commits
eaf39ea
f3e6b99
f535234
a78fe85
bf5317e
a6061bd
a37a6e4
05e4818
eb8e753
e9e48ab
1a8b847
763821f
77f814f
741ff5a
55e6eb8
45798dd
327da6f
eb00272
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import React, {useCallback, useEffect} from 'react'; | ||
import Animated, {runOnJS, useAnimatedStyle, useSharedValue, withDelay, withTiming} from 'react-native-reanimated'; | ||
import Text from '@components/Text'; | ||
import useThemeStyles from '@hooks/useThemeStyles'; | ||
import variables from '@styles/variables'; | ||
import SettlementButton from '.'; | ||
import type SettlementButtonProps from './types'; | ||
|
||
type AnimatedSettlementButtonProps = SettlementButtonProps & { | ||
shouldStartPaidAnimation: boolean; | ||
onAnimationFinish: () => void; | ||
}; | ||
|
||
function AnimatedSettlementButton({shouldStartPaidAnimation, onAnimationFinish, isDisabled, ...settlementButtonProps}: AnimatedSettlementButtonProps) { | ||
const styles = useThemeStyles(); | ||
const buttonScale = useSharedValue(1); | ||
const buttonOpacity = useSharedValue(1); | ||
const paymentCompleteTextScale = useSharedValue(0); | ||
const paymentCompleteTextOpacity = useSharedValue(1); | ||
const height = useSharedValue<number>(variables.componentSizeNormal); | ||
const buttonStyles = useAnimatedStyle(() => ({ | ||
transform: [{scale: buttonScale.value}], | ||
opacity: buttonOpacity.value, | ||
})); | ||
const paymentCompleteTextStyles = useAnimatedStyle(() => ({ | ||
transform: [{scale: paymentCompleteTextScale.value}], | ||
opacity: paymentCompleteTextOpacity.value, | ||
position: 'absolute', | ||
alignSelf: 'center', | ||
})); | ||
|
||
const containerStyles = useAnimatedStyle(() => ({ | ||
height: height.value, | ||
justifyContent: 'center', | ||
overflow: 'hidden', | ||
})); | ||
|
||
const buttonDisabledStyle = shouldStartPaidAnimation | ||
? { | ||
opacity: 1, | ||
...styles.cursorDefault, | ||
} | ||
: undefined; | ||
|
||
const resetAnimation = useCallback(() => { | ||
// eslint-disable-next-line react-compiler/react-compiler | ||
buttonScale.value = 1; | ||
buttonOpacity.value = 1; | ||
paymentCompleteTextScale.value = 0; | ||
paymentCompleteTextOpacity.value = 1; | ||
height.value = variables.componentSizeNormal; | ||
}, [buttonScale, buttonOpacity, paymentCompleteTextScale, paymentCompleteTextOpacity, height]); | ||
|
||
useEffect(() => { | ||
if (!shouldStartPaidAnimation) { | ||
resetAnimation(); | ||
return; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I reset the animation here in case of a pay error. error.mp4 |
||
// eslint-disable-next-line react-compiler/react-compiler | ||
buttonScale.value = withTiming(0, {duration: 200}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's create constants for 200 and 1200 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
buttonOpacity.value = withTiming(0, {duration: 200}); | ||
paymentCompleteTextScale.value = withTiming(1, {duration: 200}); | ||
|
||
// Wait for the above animation + 1s delay before hiding the component | ||
height.value = withDelay( | ||
1200, | ||
withTiming(0, {duration: 200}, () => runOnJS(onAnimationFinish)()), | ||
); | ||
paymentCompleteTextOpacity.value = withDelay(1200, withTiming(0, {duration: 200})); | ||
}, [shouldStartPaidAnimation, onAnimationFinish, buttonOpacity, buttonScale, height, paymentCompleteTextOpacity, paymentCompleteTextScale, resetAnimation]); | ||
|
||
return ( | ||
<Animated.View style={containerStyles}> | ||
{shouldStartPaidAnimation && ( | ||
<Animated.View style={paymentCompleteTextStyles}> | ||
<Text style={[styles.buttonMediumText]}>Payment complete</Text> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should've translated this |
||
</Animated.View> | ||
)} | ||
<Animated.View style={buttonStyles}> | ||
<SettlementButton | ||
// eslint-disable-next-line react/jsx-props-no-spreading | ||
{...settlementButtonProps} | ||
isDisabled={shouldStartPaidAnimation || isDisabled} | ||
disabledStyle={buttonDisabledStyle} | ||
/> | ||
</Animated.View> | ||
</Animated.View> | ||
); | ||
} | ||
|
||
AnimatedSettlementButton.displayName = 'AnimatedSettlementButton'; | ||
|
||
export default AnimatedSettlementButton; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes look good !
The only thing I would prefer
Is to pass only ref and use the state inside AnimatedSettlementButton since stopAnimation looks redundant when we pass this function
But it's optional
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think using ref is a good idea since it won't trigger a re-render.