Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

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