-
Notifications
You must be signed in to change notification settings - Fork 97
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(points): add number ticker for balance (#5345)
### Description As the title. The approach was largely inspired by [a nice man on youtube](https://www.youtube.com/watch?v=nhshusc5ya0&list=PL7ycojg9_ZoJwnayMF8n-VlhdXnE_grdg&index=1). I've added support for decimals in case we want to use this for the wallet balance. The ticker animation will run any time the value changes, so it will work automatically for when the points balance changes and the user remains on the screen. The downside with animations is that there's a lot of smoke and mirrors that mess with the dom. For this ticker, we're basically animating columns of text up and down. From the dom it no longer looks like the actual balance and we lose the ability to assert on its value from the unit tests. ### Test plan Without decimals: https://github.com/valora-inc/wallet/assets/20150449/a9ce407e-544e-4a69-85c1-9219f629360b With decimals: https://github.com/valora-inc/wallet/assets/20150449/4128f00a-0949-4321-98ba-8dda0cd23c89 ### Related issues - Fixes RET-1072 ### Backwards compatibility Y ### Network scalability If a new NetworkId and/or Network are added in the future, the changes in this PR will: - [x] Continue to work without code changes, OR trigger a compilation error (guaranteeing we find it when a new network is added)
- Loading branch information
1 parent
2bbc26b
commit 3846796
Showing
3 changed files
with
123 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import * as React from 'react' | ||
import { Animated, StyleProp, StyleSheet, Text, TextStyle, View } from 'react-native' | ||
import { typeScale } from 'src/styles/fonts' | ||
|
||
interface CommonProps { | ||
textHeight: number | ||
textStyles?: StyleProp<TextStyle> | ||
animationDuration?: number | ||
} | ||
interface Props extends CommonProps { | ||
finalValue: string | ||
testID?: string | ||
} | ||
|
||
interface TickProps extends CommonProps { | ||
startValue: number | ||
endValue: number | ||
} | ||
|
||
interface TickTextProps extends CommonProps { | ||
value: string | ||
} | ||
|
||
const numberRange = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] | ||
|
||
function TickText({ value, textHeight, textStyles }: TickTextProps) { | ||
return ( | ||
<View style={[styles.tickText, { height: textHeight }]}> | ||
<Text style={[styles.text, textStyles]}>{value}</Text> | ||
</View> | ||
) | ||
} | ||
|
||
function Tick({ startValue, endValue, textHeight, textStyles, animationDuration }: TickProps) { | ||
const animatedValue = new Animated.Value(startValue * textHeight * -1) | ||
const transformStyle = { transform: [{ translateY: animatedValue }] } | ||
const duration = animationDuration ?? 1300 | ||
|
||
Animated.timing(animatedValue, { | ||
toValue: endValue * textHeight * -1, | ||
duration, | ||
useNativeDriver: true, | ||
}).start() | ||
|
||
return ( | ||
<Animated.View style={[transformStyle]}> | ||
{numberRange.map((number, index) => { | ||
return ( | ||
<TickText key={index} textHeight={textHeight} textStyles={textStyles} value={number} /> | ||
) | ||
})} | ||
</Animated.View> | ||
) | ||
} | ||
|
||
export default function NumberTicker({ | ||
finalValue, | ||
textStyles, | ||
textHeight, | ||
animationDuration, | ||
testID, | ||
}: Props) { | ||
const finalValueArray = finalValue.toString().split('') | ||
|
||
// For the startValueArray, map over each character in the finalValueArray to | ||
// replace digits with random digits, do not change non-digit characters (e.g. | ||
// decimal separator) | ||
const startValueArray = finalValueArray.map((char) => { | ||
return char.match(/\d/) ? Math.floor(Math.random() * 10).toString() : char | ||
}) | ||
|
||
return ( | ||
<View style={[styles.container, { height: textHeight }]} testID={testID}> | ||
{finalValueArray.map((value, index) => { | ||
// If the character is not a digit, render it as a static text element | ||
if (!value.match(/\d/)) { | ||
return ( | ||
<TickText key={index} textHeight={textHeight} textStyles={textStyles} value={value} /> | ||
) | ||
} | ||
|
||
const endValue = parseInt(value, 10) | ||
const startValue = parseInt(startValueArray[index], 10) | ||
return ( | ||
<Tick | ||
key={`${value}-${index}-${startValueArray[index]}`} | ||
startValue={startValue} | ||
endValue={endValue} | ||
textHeight={textHeight} | ||
textStyles={textStyles} | ||
animationDuration={animationDuration} | ||
/> | ||
) | ||
})} | ||
</View> | ||
) | ||
} | ||
|
||
const styles = StyleSheet.create({ | ||
container: { | ||
overflow: 'hidden', | ||
flexDirection: 'row', | ||
// This negative gap is a hack to bring the numbers closer together, | ||
// otherwise they feel unnatural and far apart | ||
gap: -2, | ||
}, | ||
tickText: { | ||
alignItems: 'center', | ||
justifyContent: 'center', | ||
}, | ||
text: { | ||
...typeScale.displaySmall, | ||
}, | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters