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