diff options
Diffstat (limited to 'src/components/dms/EmojiReactionPicker.tsx')
-rw-r--r-- | src/components/dms/EmojiReactionPicker.tsx | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/src/components/dms/EmojiReactionPicker.tsx b/src/components/dms/EmojiReactionPicker.tsx new file mode 100644 index 000000000..a98cebf9a --- /dev/null +++ b/src/components/dms/EmojiReactionPicker.tsx @@ -0,0 +1,118 @@ +import {useMemo, useState} from 'react' +import {Alert, useWindowDimensions, View} from 'react-native' +import {type ChatBskyConvoDefs} from '@atproto/api' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {useSession} from '#/state/session' +import {atoms as a, tokens, useTheme} from '#/alf' +import * as ContextMenu from '#/components/ContextMenu' +import { + useContextMenuContext, + useContextMenuMenuContext, +} from '#/components/ContextMenu/context' +import { + EmojiHeartEyes_Stroke2_Corner0_Rounded as EmojiHeartEyesIcon, + EmojiSmile_Stroke2_Corner0_Rounded as EmojiSmileIcon, +} from '#/components/icons/Emoji' +import {type TriggerProps} from '#/components/Menu/types' +import {Text} from '#/components/Typography' +import {EmojiPopup} from './EmojiPopup' + +export function EmojiReactionPicker({ + message, +}: { + message: ChatBskyConvoDefs.MessageView + children?: TriggerProps['children'] +}) { + const {_} = useLingui() + const {currentAccount} = useSession() + const t = useTheme() + const isFromSelf = message.sender?.did === currentAccount?.did + const {measurement, close} = useContextMenuContext() + const {align} = useContextMenuMenuContext() + const [layout, setLayout] = useState({width: 0, height: 0}) + const {width: screenWidth} = useWindowDimensions() + + // 1 in 100 chance of showing heart eyes icon + const EmojiIcon = useMemo(() => { + return Math.random() < 0.01 ? EmojiHeartEyesIcon : EmojiSmileIcon + }, []) + + const handleEmojiSelect = (emoji: string) => { + Alert.alert(emoji) + } + + const position = useMemo(() => { + return { + x: align === 'left' ? 12 : screenWidth - layout.width - 12, + y: (measurement?.y ?? 0) - tokens.space.xs - layout.height, + height: layout.height, + width: layout.width, + } + }, [measurement, align, screenWidth, layout]) + + return ( + <View + onLayout={evt => setLayout(evt.nativeEvent.layout)} + style={[ + a.rounded_full, + a.absolute, + {bottom: '100%'}, + isFromSelf ? a.right_0 : a.left_0, + t.scheme === 'light' ? t.atoms.bg : t.atoms.bg_contrast_25, + a.flex_row, + a.p_xs, + a.gap_xs, + a.mb_xs, + a.z_20, + a.border, + t.atoms.border_contrast_low, + a.shadow_md, + ]}> + {['👍', '😆', '❤️', '👀', '😢'].map(emoji => ( + <ContextMenu.Item + position={position} + label={_(msg`React with ${emoji}`)} + key={emoji} + onPress={() => handleEmojiSelect(emoji)} + unstyled> + {hovered => ( + <View + style={[ + a.rounded_full, + hovered && {backgroundColor: t.palette.primary_500}, + {height: 40, width: 40}, + a.justify_center, + a.align_center, + ]}> + <Text style={[a.text_center, {fontSize: 30}]} emoji> + {emoji} + </Text> + </View> + )} + </ContextMenu.Item> + ))} + <EmojiPopup + onEmojiSelected={emoji => { + close() + handleEmojiSelect(emoji) + }}> + <View + style={[ + a.rounded_full, + t.scheme === 'light' + ? t.atoms.bg_contrast_25 + : t.atoms.bg_contrast_50, + {height: 40, width: 40}, + a.justify_center, + a.align_center, + a.border, + t.atoms.border_contrast_low, + ]}> + <EmojiIcon size="xl" fill={t.palette.contrast_400} /> + </View> + </EmojiPopup> + </View> + ) +} |