Bluesky app fork with some witchin' additions 馃挮
at main 122 lines 3.6 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 {EventStopper} from '#/view/com/util/EventStopper' 20import {native} from '#/alf' 21import {ArrowOutOfBoxModified_Stroke2_Corner2_Rounded as ArrowOutOfBoxIcon} from '#/components/icons/ArrowOutOfBox' 22import {ArrowShareRight_Stroke2_Corner2_Rounded as ArrowShareRightIcon} from '#/components/icons/ArrowShareRight' 23import {useMenuControl} from '#/components/Menu' 24import * as Menu from '#/components/Menu' 25import {PostControlButton, PostControlButtonIcon} from '../PostControlButton' 26import {ShareMenuItems} from './ShareMenuItems' 27 28let ShareMenuButton = ({ 29 testID, 30 post, 31 big, 32 record, 33 richText, 34 timestamp, 35 threadgateRecord, 36 onShare, 37 hitSlop, 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}): React.ReactNode => { 49 const {_} = useLingui() 50 const gate = useGate() 51 52 const ShareIcon = gate('alt_share_icon') 53 ? ArrowShareRightIcon 54 : ArrowOutOfBoxIcon 55 56 const menuControl = useMenuControl() 57 const [hasBeenOpen, setHasBeenOpen] = useState(false) 58 const lazyMenuControl = useMemo( 59 () => ({ 60 ...menuControl, 61 open() { 62 setHasBeenOpen(true) 63 // HACK. We need the state update to be flushed by the time 64 // menuControl.open() fires but RN doesn't expose flushSync. 65 setTimeout(menuControl.open) 66 67 logger.metric( 68 'share:open', 69 {context: big ? 'thread' : 'feed'}, 70 {statsig: true}, 71 ) 72 }, 73 }), 74 [menuControl, setHasBeenOpen, big], 75 ) 76 77 const onNativeLongPress = () => { 78 logger.metric('share:press:nativeShare', {}, {statsig: true}) 79 const urip = new AtUri(post.uri) 80 const href = makeProfileLink(post.author, 'post', urip.rkey) 81 const url = toShareUrl(href) 82 shareUrl(url) 83 onShare() 84 } 85 86 return ( 87 <EventStopper onKeyDown={false}> 88 <Menu.Root control={lazyMenuControl}> 89 <Menu.Trigger label={_(msg`Open share menu`)}> 90 {({props}) => { 91 return ( 92 <PostControlButton 93 testID="postShareBtn" 94 big={big} 95 label={props.accessibilityLabel} 96 {...props} 97 onLongPress={native(onNativeLongPress)} 98 hitSlop={hitSlop}> 99 <PostControlButtonIcon icon={ShareIcon} /> 100 </PostControlButton> 101 ) 102 }} 103 </Menu.Trigger> 104 {hasBeenOpen && ( 105 // Lazily initialized. Once mounted, they stay mounted. 106 <ShareMenuItems 107 testID={testID} 108 post={post} 109 record={record} 110 richText={richText} 111 timestamp={timestamp} 112 threadgateRecord={threadgateRecord} 113 onShare={onShare} 114 /> 115 )} 116 </Menu.Root> 117 </EventStopper> 118 ) 119} 120 121ShareMenuButton = memo(ShareMenuButton) 122export {ShareMenuButton}