Changeset View
Changeset View
Standalone View
Standalone View
native/chat/reaction-selection-popover.react.js
// @flow | // @flow | ||||
import invariant from 'invariant'; | |||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import { View, TouchableOpacity, Text } from 'react-native'; | import { View, TouchableOpacity, Text } from 'react-native'; | ||||
import Animated from 'react-native-reanimated'; | |||||
import { useReactionSelectionPopoverPosition } from './reaction-message-utils.js'; | import { useReactionSelectionPopoverPosition } from './reaction-message-utils.js'; | ||||
import SWMansionIcon from '../components/swmansion-icon.react.js'; | import SWMansionIcon from '../components/swmansion-icon.react.js'; | ||||
import type { AppNavigationProp } from '../navigation/app-navigator.react.js'; | import type { AppNavigationProp } from '../navigation/app-navigator.react.js'; | ||||
import { OverlayContext } from '../navigation/overlay-context.js'; | |||||
import type { TooltipModalParamList } from '../navigation/route-names.js'; | import type { TooltipModalParamList } from '../navigation/route-names.js'; | ||||
import { useStyles } from '../themes/colors.js'; | import { useStyles } from '../themes/colors.js'; | ||||
import { useTooltipActions } from '../tooltip/tooltip-hooks.js'; | import { useTooltipActions } from '../tooltip/tooltip-hooks.js'; | ||||
import type { TooltipRoute } from '../tooltip/tooltip.react.js'; | import type { TooltipRoute } from '../tooltip/tooltip.react.js'; | ||||
import { AnimatedView } from '../types/styles.js'; | |||||
type Props<RouteName: $Keys<TooltipModalParamList>> = { | type Props<RouteName: $Keys<TooltipModalParamList>> = { | ||||
+navigation: AppNavigationProp<RouteName>, | +navigation: AppNavigationProp<RouteName>, | ||||
+route: TooltipRoute<RouteName>, | +route: TooltipRoute<RouteName>, | ||||
+openEmojiPicker: () => mixed, | +openEmojiPicker: () => mixed, | ||||
+sendReaction: (reaction: string) => mixed, | +sendReaction: (reaction: string) => mixed, | ||||
}; | }; | ||||
/* eslint-disable import/no-named-as-default-member */ | |||||
const { Extrapolate, interpolateNode } = Animated; | |||||
/* eslint-enable import/no-named-as-default-member */ | |||||
function ReactionSelectionPopover<RouteName: $Keys<TooltipModalParamList>>( | function ReactionSelectionPopover<RouteName: $Keys<TooltipModalParamList>>( | ||||
props: Props<RouteName>, | props: Props<RouteName>, | ||||
): React.Node { | ): React.Node { | ||||
const { navigation, route, openEmojiPicker, sendReaction } = props; | const { navigation, route, openEmojiPicker, sendReaction } = props; | ||||
const { verticalBounds, initialCoordinates, margin } = route.params; | const { verticalBounds, initialCoordinates, margin } = route.params; | ||||
const { containerStyle: popoverContainerStyle } = | const { containerStyle: popoverContainerStyle } = | ||||
useReactionSelectionPopoverPosition({ | useReactionSelectionPopoverPosition({ | ||||
initialCoordinates, | initialCoordinates, | ||||
verticalBounds, | verticalBounds, | ||||
margin, | margin, | ||||
}); | }); | ||||
const overlayContext = React.useContext(OverlayContext); | |||||
invariant( | |||||
overlayContext, | |||||
'ReactionSelectionPopover should have OverlayContext', | |||||
); | |||||
const { position } = overlayContext; | |||||
const animationStyle = React.useMemo( | |||||
() => ({ | |||||
opacity: interpolateNode(position, { | |||||
inputRange: [0, 0.1], | |||||
outputRange: [0, 1], | |||||
extrapolate: Extrapolate.CLAMP, | |||||
}), | |||||
transform: [ | |||||
{ | |||||
scale: interpolateNode(position, { | |||||
inputRange: [0.2, 0.8], | |||||
outputRange: [0, 1], | |||||
extrapolate: Extrapolate.CLAMP, | |||||
}), | |||||
}, | |||||
], | |||||
}), | |||||
[position], | |||||
); | |||||
const styles = useStyles(unboundStyles); | const styles = useStyles(unboundStyles); | ||||
const containerStyle = React.useMemo( | const containerStyle = React.useMemo( | ||||
() => [styles.reactionSelectionPopoverContainer, popoverContainerStyle], | () => ({ | ||||
[popoverContainerStyle, styles.reactionSelectionPopoverContainer], | ...styles.reactionSelectionPopoverContainer, | ||||
...popoverContainerStyle, | |||||
...animationStyle, | |||||
}), | |||||
[ | |||||
popoverContainerStyle, | |||||
styles.reactionSelectionPopoverContainer, | |||||
animationStyle, | |||||
], | |||||
); | ); | ||||
const tooltipRouteKey = route.key; | const tooltipRouteKey = route.key; | ||||
const { hideTooltip, dismissTooltip } = useTooltipActions( | const { hideTooltip, dismissTooltip } = useTooltipActions( | ||||
navigation, | navigation, | ||||
tooltipRouteKey, | tooltipRouteKey, | ||||
); | ); | ||||
Show All 22 Lines | const defaultEmojis = React.useMemo(() => { | ||||
)); | )); | ||||
}, [ | }, [ | ||||
onPressDefaultEmoji, | onPressDefaultEmoji, | ||||
styles.reactionSelectionItemContainer, | styles.reactionSelectionItemContainer, | ||||
styles.reactionSelectionItemEmoji, | styles.reactionSelectionItemEmoji, | ||||
]); | ]); | ||||
return ( | return ( | ||||
<View style={containerStyle}> | <AnimatedView style={containerStyle}> | ||||
{defaultEmojis} | {defaultEmojis} | ||||
<TouchableOpacity onPress={onPressEmojiKeyboardButton}> | <TouchableOpacity onPress={onPressEmojiKeyboardButton}> | ||||
<View style={styles.emojiKeyboardButtonContainer}> | <View style={styles.emojiKeyboardButtonContainer}> | ||||
<SWMansionIcon name="plus" style={styles.icon} size={18} /> | <SWMansionIcon name="plus" style={styles.icon} size={18} /> | ||||
</View> | </View> | ||||
</TouchableOpacity> | </TouchableOpacity> | ||||
</View> | </AnimatedView> | ||||
); | ); | ||||
} | } | ||||
const unboundStyles = { | const unboundStyles = { | ||||
reactionSelectionPopoverContainer: { | reactionSelectionPopoverContainer: { | ||||
flexDirection: 'row', | flexDirection: 'row', | ||||
alignItems: 'center', | alignItems: 'center', | ||||
backgroundColor: 'tooltipBackground', | backgroundColor: 'tooltipBackground', | ||||
Show All 32 Lines |