mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import {useState} from 'react'
2import {Pressable, View} from 'react-native'
3import {type ChatBskyConvoDefs} from '@atproto/api'
4import EmojiPicker from '@emoji-mart/react'
5import {msg} from '@lingui/macro'
6import {useLingui} from '@lingui/react'
7import {DropdownMenu} from 'radix-ui'
8
9import {useSession} from '#/state/session'
10import {type Emoji} from '#/view/com/composer/text-input/web/EmojiPicker'
11import {useWebPreloadEmoji} from '#/view/com/composer/text-input/web/useWebPreloadEmoji'
12import {atoms as a, flatten, useTheme} from '#/alf'
13import {DotGrid_Stroke2_Corner0_Rounded as DotGridIcon} from '#/components/icons/DotGrid'
14import * as Menu from '#/components/Menu'
15import {type TriggerProps} from '#/components/Menu/types'
16import {Text} from '#/components/Typography'
17import {hasAlreadyReacted, hasReachedReactionLimit} from './util'
18
19export function EmojiReactionPicker({
20 message,
21 children,
22 onEmojiSelect,
23}: {
24 message: ChatBskyConvoDefs.MessageView
25 children?: TriggerProps['children']
26 onEmojiSelect: (emoji: string) => void
27}) {
28 if (!children)
29 throw new Error('EmojiReactionPicker requires the children prop on web')
30
31 const {_} = useLingui()
32
33 return (
34 <Menu.Root>
35 <Menu.Trigger label={_(msg`Add emoji reaction`)}>{children}</Menu.Trigger>
36 <MenuInner message={message} onEmojiSelect={onEmojiSelect} />
37 </Menu.Root>
38 )
39}
40
41function MenuInner({
42 message,
43 onEmojiSelect,
44}: {
45 message: ChatBskyConvoDefs.MessageView
46 onEmojiSelect: (emoji: string) => void
47}) {
48 const t = useTheme()
49 const {control} = Menu.useMenuContext()
50 const {currentAccount} = useSession()
51
52 useWebPreloadEmoji({immediate: true})
53
54 const [expanded, setExpanded] = useState(false)
55
56 const [prevOpen, setPrevOpen] = useState(control.isOpen)
57
58 if (control.isOpen !== prevOpen) {
59 setPrevOpen(control.isOpen)
60 if (!control.isOpen) {
61 setExpanded(false)
62 }
63 }
64
65 const handleEmojiPickerResponse = (emoji: Emoji) => {
66 handleEmojiSelect(emoji.native)
67 }
68
69 const handleEmojiSelect = (emoji: string) => {
70 control.close()
71 onEmojiSelect(emoji)
72 }
73
74 const limitReacted = hasReachedReactionLimit(message, currentAccount?.did)
75
76 return expanded ? (
77 <DropdownMenu.Portal>
78 <DropdownMenu.Content
79 sideOffset={5}
80 collisionPadding={{left: 5, right: 5, bottom: 5}}>
81 <div onWheel={evt => evt.stopPropagation()}>
82 <EmojiPicker
83 onEmojiSelect={handleEmojiPickerResponse}
84 autoFocus={true}
85 />
86 </div>
87 </DropdownMenu.Content>
88 </DropdownMenu.Portal>
89 ) : (
90 <Menu.Outer style={[a.rounded_full]}>
91 <View style={[a.flex_row, a.gap_xs]}>
92 {['👍', '😆', '❤️', '👀', '😢'].map(emoji => {
93 const alreadyReacted = hasAlreadyReacted(
94 message,
95 currentAccount?.did,
96 emoji,
97 )
98 return (
99 <DropdownMenu.Item
100 key={emoji}
101 className={[
102 'EmojiReactionPicker__Pressable',
103 alreadyReacted && '__selected',
104 limitReacted && '__disabled',
105 ]
106 .filter(Boolean)
107 .join(' ')}
108 onSelect={() => handleEmojiSelect(emoji)}
109 style={flatten([
110 a.flex,
111 a.flex_col,
112 a.rounded_full,
113 a.justify_center,
114 a.align_center,
115 a.transition_transform,
116 {
117 width: 34,
118 height: 34,
119 },
120 alreadyReacted && {
121 backgroundColor: t.atoms.bg_contrast_100.backgroundColor,
122 },
123 ])}>
124 <Text style={[a.text_center, {fontSize: 28}]} emoji>
125 {emoji}
126 </Text>
127 </DropdownMenu.Item>
128 )
129 })}
130 <DropdownMenu.Item
131 asChild
132 className="EmojiReactionPicker__PickerButton">
133 <Pressable
134 accessibilityRole="button"
135 role="button"
136 onPress={() => setExpanded(true)}
137 style={flatten([
138 a.rounded_full,
139 {height: 34, width: 34},
140 a.justify_center,
141 a.align_center,
142 ])}>
143 <DotGridIcon size="lg" style={t.atoms.text_contrast_medium} />
144 </Pressable>
145 </DropdownMenu.Item>
146 </View>
147 </Menu.Outer>
148 )
149}