diff --git a/native/chat/reaction-message-utils.js b/native/chat/reaction-message-utils.js --- a/native/chat/reaction-message-utils.js +++ b/native/chat/reaction-message-utils.js @@ -119,4 +119,4 @@ ); } -export { onPressReact }; +export { onPressReact, sendReaction }; diff --git a/native/chat/reaction-selection-popover.react.js b/native/chat/reaction-selection-popover.react.js new file mode 100644 --- /dev/null +++ b/native/chat/reaction-selection-popover.react.js @@ -0,0 +1,90 @@ +// @flow + +import * as React from 'react'; +import { View, TouchableOpacity, Text } from 'react-native'; + +import type { SetState } from 'lib/types/hook-types'; + +import { useStyles } from '../themes/colors'; +import type { LayoutEvent } from '../types/react-native'; +import { AnimatedView, type AnimatedViewStyle } from '../types/styles'; + +type ReactionSelectionPopoverProps = { + +onEmojiSelected: ({ emoji: string }) => void, + +setHideTooltip: SetState, + +reactionSelectionPopoverContainerStyle: AnimatedViewStyle, + +onTooltipContainerLayout: (event: LayoutEvent) => void, +}; + +function ReactionSelectionPopover( + props: ReactionSelectionPopoverProps, +): React.Node { + const { + onEmojiSelected, + setHideTooltip, + reactionSelectionPopoverContainerStyle, + onTooltipContainerLayout, + } = props; + + const styles = useStyles(unboundStyles); + + const onPressDefaultEmoji = React.useCallback( + (emoji: string) => { + onEmojiSelected({ emoji }); + setHideTooltip(true); + }, + [onEmojiSelected, setHideTooltip], + ); + + const defaultEmojis = React.useMemo(() => { + const defaultEmojisData = ['❤️', '😆', '😮', '😠', '👍']; + + return defaultEmojisData.map(emoji => ( + onPressDefaultEmoji(emoji)}> + + {emoji} + + + )); + }, [ + onPressDefaultEmoji, + styles.reactionSelectionItemContainer, + styles.reactionSelectionItemEmoji, + ]); + + return ( + + + {defaultEmojis} + + + ); +} + +const unboundStyles = { + reactionSelectionPopoverContainer: { + flexDirection: 'row', + backgroundColor: 'tooltipBackground', + padding: 8, + borderRadius: 8, + flex: 1, + }, + reactionSelectionItemContainer: { + backgroundColor: 'reactionSelectionPopoverItemBackground', + justifyContent: 'center', + alignItems: 'center', + padding: 8, + borderRadius: 20, + width: 40, + height: 40, + marginRight: 12, + }, + reactionSelectionItemEmoji: { + fontSize: 18, + }, +}; + +export default ReactionSelectionPopover; diff --git a/native/navigation/tooltip.react.js b/native/navigation/tooltip.react.js --- a/native/navigation/tooltip.react.js +++ b/native/navigation/tooltip.react.js @@ -42,6 +42,8 @@ } from 'lib/utils/action-utils'; import { ChatContext, type ChatContextType } from '../chat/chat-context'; +import { sendReaction } from '../chat/reaction-message-utils'; +import ReactionSelectionPopover from '../chat/reaction-selection-popover.react'; import CommIcon from '../components/comm-icon.react'; import { SingleLine } from '../components/single-line.react'; import SWMansionIcon from '../components/swmansion-icon.react'; @@ -614,6 +616,21 @@ ); } + let reactionSelectionPopover = null; + + if (tooltipLocation === 'fixed' && canCreateReactionFromMessage) { + reactionSelectionPopover = ( + + ); + } + return ( @@ -623,12 +640,48 @@ + {reactionSelectionPopover} {tooltip} ); } + onEmojiSelected = emoji => { + const dispatchFunctions = { + dispatch: this.props.dispatch, + dispatchActionPromise: this.props.dispatchActionPromise, + }; + + const viewerID = this.props.viewerID; + invariant(viewerID, 'viewerID should be set'); + + const messageID = this.props.route.params.item?.messageInfo.id; + invariant(messageID, 'messageID should be set'); + + const threadID = this.props.route.params.item?.threadInfo.id; + invariant(threadID, 'threadID should be set'); + + const reactionInput = emoji.emoji; + const viewerReacted = !!this.props.route.params.item?.reactions.get( + reactionInput, + )?.viewerReacted; + const action = viewerReacted ? 'remove_reaction' : 'add_reaction'; + + const localID = `${localIDPrefix}${this.props.nextReactionMessageLocalID}`; + + sendReaction( + messageID, + localID, + threadID, + reactionInput, + action, + dispatchFunctions, + this.bindServerCall, + viewerID, + ); + }; + onPressBackdrop = () => { if (this.tooltipLocation !== 'fixed') { this.props.navigation.goBackOnce(); diff --git a/native/themes/colors.js b/native/themes/colors.js --- a/native/themes/colors.js +++ b/native/themes/colors.js @@ -74,6 +74,7 @@ panelSecondaryForegroundBorder: '#D1D1D6', purpleLink: '#7E57C2', purpleButton: '#7E57C2', + reactionSelectionPopoverItemBackground: '#404040', redButton: '#BB8888', redText: '#FF4444', spoiler: '#33332C', @@ -164,6 +165,7 @@ panelSecondaryForegroundBorder: '#666666', purpleLink: '#AE94DB', purpleButton: '#7E57C2', + reactionSelectionPopoverItemBackground: '#404040', redButton: '#FF4444', redText: '#FF4444', spoiler: '#33332C',