Bluesky app fork with some witchin' additions 馃挮
at readme-update 131 lines 3.7 kB view raw
1import {memo, useMemo, useState} from 'react' 2import {type Insets} from 'react-native' 3import { 4 type AppBskyFeedDefs, 5 type AppBskyFeedPost, 6 type AppBskyFeedThreadgate, 7 AtUri, 8 type RichText as RichTextAPI, 9} from '@atproto/api' 10import {msg} from '@lingui/macro' 11import {useLingui} from '@lingui/react' 12 13import {makeProfileLink} from '#/lib/routes/links' 14import {shareUrl} from '#/lib/sharing' 15import {toShareUrl} from '#/lib/strings/url-helpers' 16import {type Shadow} from '#/state/cache/post-shadow' 17import {useFeedFeedbackContext} from '#/state/feed-feedback' 18import {EventStopper} from '#/view/com/util/EventStopper' 19import {native} from '#/alf' 20import {ArrowShareRight_Stroke2_Corner2_Rounded as ArrowShareRightIcon} from '#/components/icons/ArrowShareRight' 21import {useMenuControl} from '#/components/Menu' 22import * as Menu from '#/components/Menu' 23import {useAnalytics} from '#/analytics' 24import {PostControlButton, PostControlButtonIcon} from '../PostControlButton' 25import {ShareMenuItems} from './ShareMenuItems' 26 27let ShareMenuButton = ({ 28 testID, 29 post, 30 big, 31 record, 32 richText, 33 timestamp, 34 threadgateRecord, 35 onShare, 36 hitSlop, 37 logContext, 38}: { 39 testID: string 40 post: Shadow<AppBskyFeedDefs.PostView> 41 big?: boolean 42 record: AppBskyFeedPost.Record 43 richText: RichTextAPI 44 timestamp: string 45 threadgateRecord?: AppBskyFeedThreadgate.Record 46 onShare: () => void 47 hitSlop?: Insets 48 logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo' 49}): React.ReactNode => { 50 const ax = useAnalytics() 51 const {_} = useLingui() 52 const {feedDescriptor} = useFeedFeedbackContext() 53 54 const menuControl = useMenuControl() 55 const [hasBeenOpen, setHasBeenOpen] = useState(false) 56 const lazyMenuControl = useMemo( 57 () => ({ 58 ...menuControl, 59 open() { 60 setHasBeenOpen(true) 61 // HACK. We need the state update to be flushed by the time 62 // menuControl.open() fires but RN doesn't expose flushSync. 63 setTimeout(menuControl.open) 64 65 ax.metric('post:share', { 66 uri: post.uri, 67 authorDid: post.author.did, 68 logContext, 69 feedDescriptor, 70 postContext: big ? 'thread' : 'feed', 71 }) 72 }, 73 }), 74 [ 75 ax, 76 menuControl, 77 setHasBeenOpen, 78 big, 79 logContext, 80 feedDescriptor, 81 post.uri, 82 post.author.did, 83 ], 84 ) 85 86 const onNativeLongPress = () => { 87 ax.metric('share:press:nativeShare', {}) 88 const urip = new AtUri(post.uri) 89 const href = makeProfileLink(post.author, 'post', urip.rkey) 90 const url = toShareUrl(href) 91 shareUrl(url) 92 onShare() 93 } 94 95 return ( 96 <EventStopper onKeyDown={false}> 97 <Menu.Root control={lazyMenuControl}> 98 <Menu.Trigger label={_(msg`Open share menu`)}> 99 {({props}) => { 100 return ( 101 <PostControlButton 102 testID="postShareBtn" 103 big={big} 104 label={props.accessibilityLabel} 105 {...props} 106 onLongPress={native(onNativeLongPress)} 107 hitSlop={hitSlop}> 108 <PostControlButtonIcon icon={ArrowShareRightIcon} /> 109 </PostControlButton> 110 ) 111 }} 112 </Menu.Trigger> 113 {hasBeenOpen && ( 114 // Lazily initialized. Once mounted, they stay mounted. 115 <ShareMenuItems 116 testID={testID} 117 post={post} 118 record={record} 119 richText={richText} 120 timestamp={timestamp} 121 threadgateRecord={threadgateRecord} 122 onShare={onShare} 123 /> 124 )} 125 </Menu.Root> 126 </EventStopper> 127 ) 128} 129 130ShareMenuButton = memo(ShareMenuButton) 131export {ShareMenuButton}