Created
September 22, 2021 10:29
-
-
Save dilipsuthar97/bf546531b8a0c789f64618756769dbfc to your computer and use it in GitHub Desktop.
This file contains hidden or 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
import * as React from 'react'; | |
import { View, Text, Image, StyleSheet, Animated } from 'react-native'; | |
import { Matrics, Colors, Icons, Fonts } from '../CommonConfig'; | |
import { Tools } from '../Helpers'; | |
import Bounceable from './Bounceable'; | |
/** | |
* 'reactionFlag' value is same as in DB | |
* it's useful during sending data with API request | |
*/ | |
const reactionTypes = [ | |
{ | |
reactionType: 0, | |
reactionName: 'like', | |
reactionFlag: 'is_like', | |
}, | |
{ | |
reactionType: 3, | |
reactionName: 'prais_the_lord', | |
reactionFlag: 'is_prais_the_lord', | |
}, | |
{ | |
reactionType: 1, | |
reactionName: 'amen', | |
reactionFlag: 'is_amen', | |
}, | |
{ | |
reactionType: 2, | |
reactionName: 'hallelujah', | |
reactionFlag: 'is_hallelujah', | |
}, | |
]; | |
export default ({ onPress, style, reactionCounts = [0, 0, 0, 0] }) => { | |
const uiData = [ | |
{ | |
lable: 'Like', | |
icon: Icons.IC_LIKE, | |
color: Colors.LIKE_1, | |
colorBg: Colors.LIKE_BG_1, | |
ref: React.createRef(), | |
}, | |
{ | |
lable: 'Prais The Lord', | |
icon: Icons.IC_LIKE_2, | |
color: Colors.LIKE_2, | |
colorBg: Colors.LIKE_BG_2, | |
ref: React.createRef(), | |
}, | |
{ | |
lable: 'Amen', | |
icon: Icons.IC_LIKE_3, | |
color: Colors.LIKE_3, | |
colorBg: Colors.LIKE_BG_3, | |
ref: React.createRef(), | |
}, | |
{ | |
lable: 'Hallelujah', | |
icon: Icons.IC_LIKE_4, | |
color: Colors.LIKE_4, | |
colorBg: Colors.LIKE_BG_4, | |
ref: React.createRef(), | |
}, | |
]; | |
const containerRef = React.useRef(); | |
const hoverViewRef = React.useRef(); | |
const [activeIndex, setActiveIndex] = React.useState(0); | |
/** | |
* Function to perform action when user presses on any reaction | |
* | |
* @param {Object} value | |
*/ | |
const onPressReaction = (value) => { | |
if (onPress) | |
onPress({ | |
reactionType: value.reactionType, | |
reactionName: value.reactionName, | |
reactionFlag: value.reactionFlag, | |
}); | |
}; | |
const getMeasurement = (index) => { | |
return new Promise((resolve, reject) => { | |
uiData[index].ref.current.measureLayout( | |
containerRef.current, | |
(x, y, width, height) => { | |
resolve({ | |
x, | |
width, | |
}); | |
}, | |
); | |
}); | |
}; | |
const onLongPressIn = async (index) => { | |
setActiveIndex(index); | |
const options = await getMeasurement(index); | |
hoverViewRef.current.show(options); | |
}; | |
const onLongPressOut = () => { | |
hoverViewRef.current.hide(); | |
}; | |
return ( | |
<View ref={containerRef} style={[styles.main.container, style]}> | |
{reactionTypes.map((value, i) => ( | |
<Bounceable | |
key={`like-${i}`} | |
activeOpacity={0.5} | |
delayPressIn={0} | |
delayLongPress={150} | |
onPress={() => onPressReaction(value)} | |
onLongPressIn={() => onLongPressIn(i)} | |
onLongPressOut={() => onLongPressOut()} | |
style={styles.main.button}> | |
<View | |
ref={uiData[i].ref} | |
style={[ | |
styles.main.wrapper, | |
{ backgroundColor: uiData[i].colorBg }, | |
]}> | |
<Image | |
style={styles.main.icon} | |
source={uiData[i].icon} | |
resizeMode={'contain'} | |
/> | |
<Text | |
style={[ | |
styles.main.count, | |
{ color: uiData[i].color }, | |
]}> | |
{Tools.formatLongNumbers(reactionCounts[i])} | |
</Text> | |
</View> | |
</Bounceable> | |
))} | |
<HoverView | |
ref={hoverViewRef} | |
data={uiData} | |
index={activeIndex} | |
containerRef={containerRef} | |
/> | |
</View> | |
); | |
}; | |
const HoverView = React.forwardRef(({ data, index, containerRef }, ref) => { | |
const hoverRef = React.useRef(); | |
const hoverHeight = React.useRef(0); | |
React.useImperativeHandle(ref, () => ({ | |
show: showAnimation, | |
hide: hideAnimation, | |
})); | |
const animations = { | |
translateX: React.useRef(new Animated.Value(0)).current, | |
anim: React.useRef(new Animated.Value(0)).current, | |
}; | |
const animationStyles = { | |
container: { | |
transform: [ | |
{ | |
translateX: animations.translateX, | |
}, | |
{ | |
translateY: animations.anim.interpolate({ | |
inputRange: [0, 1], | |
outputRange: [0, -hoverHeight.current], | |
}), | |
}, | |
{ | |
scale: animations.anim, | |
}, | |
], | |
opacity: animations.anim, | |
}, | |
}; | |
const onLayout = ({ nativeEvent }) => { | |
hoverHeight.current = nativeEvent.layout.height; | |
}; | |
const getMeasurement = () => { | |
return new Promise((resolve) => { | |
hoverRef.current.measureLayout( | |
containerRef.current, | |
(x, y, width, height) => { | |
resolve({ | |
width, | |
}); | |
}, | |
); | |
}); | |
}; | |
const getTranslateXValue = async (options) => { | |
const { width } = await getMeasurement(); | |
let toValue = options.x; | |
if (width < options.width) { | |
toValue = options.x + (options.width - width) / 2; | |
} else if (width > options.width) { | |
toValue = options.x - (width - options.width) / 2; | |
} | |
return toValue; | |
}; | |
const showAnimation = async (options) => { | |
let toValue = await getTranslateXValue(options); | |
Animated.sequence([ | |
Animated.timing(animations.translateX, { | |
toValue, | |
duration: 1, | |
useNativeDriver: true, | |
}), | |
Animated.spring(animations.anim, { | |
toValue: 1, | |
useNativeDriver: true, | |
}), | |
]).start(); | |
}; | |
const hideAnimation = () => { | |
Animated.spring(animations.anim, { | |
toValue: 0, | |
useNativeDriver: true, | |
}).start(); | |
}; | |
return ( | |
<Animated.View | |
onLayout={onLayout} | |
style={[styles.hover.container, animationStyles.container]}> | |
<Animated.View | |
ref={hoverRef} | |
style={[ | |
styles.hover.wrapper, | |
{ borderColor: data[index].colorBg }, | |
]}> | |
<Image | |
style={styles.hover.icon} | |
source={data[index].icon} | |
resizeMode={'contain'} | |
/> | |
<Text | |
style={[styles.hover.lable, { color: data[index].color }]}> | |
{data[index].lable} | |
</Text> | |
</Animated.View> | |
<View | |
style={[ | |
styles.hover.triangle, | |
styles.hover.arrowDown, | |
{ borderTopColor: data[index].colorBg }, | |
]} | |
/> | |
</Animated.View> | |
); | |
}); | |
const styles = { | |
main: StyleSheet.create({ | |
container: { | |
flexDirection: 'row', | |
paddingHorizontal: Matrics.mvs(16), | |
paddingVertical: Matrics.mvs(8), | |
justifyContent: 'space-between', | |
width: '100%', | |
}, | |
button: { | |
width: '23%', | |
justifyContent: 'center', | |
}, | |
wrapper: { | |
flexDirection: 'row', | |
alignItems: 'center', | |
justifyContent: 'center', | |
borderRadius: 100, | |
}, | |
icon: { | |
width: Matrics.mvs(18), | |
height: Matrics.mvs(18), | |
}, | |
count: { | |
fontSize: Matrics.mvs(12), | |
fontFamily: Fonts.Regular, | |
padding: Matrics.mvs(8), | |
}, | |
}), | |
hover: StyleSheet.create({ | |
container: { | |
position: 'absolute', | |
alignSelf: 'center', | |
alignItems: 'center', | |
}, | |
wrapper: { | |
borderRadius: Matrics.mvs(6), | |
paddingHorizontal: Matrics.s(8), | |
paddingVertical: Matrics.vs(6), | |
backgroundColor: Colors.WHITE, | |
borderWidth: 1.5, | |
flexDirection: 'row', | |
alignItems: 'center', | |
}, | |
icon: { | |
width: Matrics.mvs(16), | |
height: Matrics.mvs(16), | |
}, | |
lable: { | |
fontSize: Matrics.mvs(12), | |
fontFamily: Fonts.Regular, | |
marginLeft: Matrics.s(6), | |
}, | |
triangle: { | |
width: 0, | |
height: 0, | |
backgroundColor: 'transparent', | |
borderStyle: 'solid', | |
marginTop: -1, | |
}, | |
arrowDown: { | |
borderTopWidth: 6, | |
borderRightWidth: 5, | |
borderBottomWidth: 0, | |
borderLeftWidth: 5, | |
borderRightColor: 'transparent', | |
borderBottomColor: 'transparent', | |
borderLeftColor: 'transparent', | |
}, | |
}), | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment