Bluesky app fork with some witchin' additions 馃挮
at main 268 lines 8.9 kB view raw
1import {memo, useMemo} from 'react' 2import * as ExpoClipboard from 'expo-clipboard' 3import {AtUri} from '@atproto/api' 4import {isIOS} from '@bsky.app/alf' 5import {msg, Trans} from '@lingui/macro' 6import {useLingui} from '@lingui/react' 7import {useNavigation} from '@react-navigation/native' 8 9import {useOpenLink} from '#/lib/hooks/useOpenLink' 10import {makeProfileLink} from '#/lib/routes/links' 11import {type NavigationProp} from '#/lib/routes/types' 12import {shareText, shareUrl} from '#/lib/sharing' 13import {toShareUrl, toShareUrlBsky} from '#/lib/strings/url-helpers' 14import {useProfileShadow} from '#/state/cache/profile-shadow' 15import {useShowExternalShareButtons} from '#/state/preferences/external-share-buttons' 16import {useSession} from '#/state/session' 17import * as Toast from '#/view/com/util/Toast' 18import {atoms as a} from '#/alf' 19import {Admonition} from '#/components/Admonition' 20import {useDialogControl} from '#/components/Dialog' 21import {SendViaChatDialog} from '#/components/dms/dialogs/ShareViaChatDialog' 22import {ArrowOutOfBoxModified_Stroke2_Corner2_Rounded as ArrowOutOfBoxIcon} from '#/components/icons/ArrowOutOfBox' 23import {ChainLink_Stroke2_Corner0_Rounded as ChainLinkIcon} from '#/components/icons/ChainLink' 24import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '#/components/icons/Clipboard' 25import {PaperPlane_Stroke2_Corner0_Rounded as PaperPlaneIcon} from '#/components/icons/PaperPlane' 26import {SquareArrowTopRight_Stroke2_Corner0_Rounded as ExternalIcon} from '#/components/icons/SquareArrowTopRight' 27import * as Menu from '#/components/Menu' 28import {useAgeAssurance} from '#/ageAssurance' 29import {useAnalytics} from '#/analytics' 30import {IS_IOS} from '#/env' 31import {useDevMode} from '#/storage/hooks/dev-mode' 32import {RecentChats} from './RecentChats' 33import {type ShareMenuItemsProps} from './ShareMenuItems.types' 34 35let ShareMenuItems = ({ 36 post, 37 onShare: onShareProp, 38}: ShareMenuItemsProps): React.ReactNode => { 39 const ax = useAnalytics() 40 const {hasSession} = useSession() 41 const {_} = useLingui() 42 const navigation = useNavigation<NavigationProp>() 43 const sendViaChatControl = useDialogControl() 44 const [devModeEnabled] = useDevMode() 45 const aa = useAgeAssurance() 46 const openLink = useOpenLink() 47 48 const postUri = post.uri 49 const postAuthor = useProfileShadow(post.author) 50 51 const href = useMemo(() => { 52 const urip = new AtUri(postUri) 53 return makeProfileLink(postAuthor, 'post', urip.rkey) 54 }, [postUri, postAuthor]) 55 56 const hideInPWI = useMemo(() => { 57 return !!postAuthor.labels?.find( 58 label => label.val === '!no-unauthenticated', 59 ) 60 }, [postAuthor]) 61 62 const onSharePost = () => { 63 ax.metric('share:press:nativeShare', {}) 64 const url = toShareUrl(href) 65 shareUrl(url) 66 onShareProp() 67 } 68 69 const onSharePostBsky = () => { 70 ax.metric('share:press:nativeShare', {}) 71 const url = toShareUrlBsky(href) 72 shareUrl(url) 73 onShareProp() 74 } 75 76 const onCopyLink = async () => { 77 ax.metric('share:press:copyLink', {}) 78 const url = toShareUrl(href) 79 if (IS_IOS) { 80 // iOS only 81 await ExpoClipboard.setUrlAsync(url) 82 } else { 83 await ExpoClipboard.setStringAsync(url) 84 } 85 Toast.show(_(msg`Copied to clipboard`), 'clipboard-check') 86 onShareProp() 87 } 88 89 const onCopyLinkBsky = async () => { 90 ax.metric('share:press:copyLink', {}) 91 const url = toShareUrlBsky(href) 92 if (isIOS) { 93 // iOS only 94 await ExpoClipboard.setUrlAsync(url) 95 } else { 96 await ExpoClipboard.setStringAsync(url) 97 } 98 Toast.show(_(msg`Copied to clipboard`), 'clipboard-check') 99 onShareProp() 100 } 101 102 const onSelectChatToShareTo = (conversation: string) => { 103 navigation.navigate('MessagesConversation', { 104 conversation, 105 embed: postUri, 106 }) 107 } 108 109 const onShareATURI = () => { 110 shareText(postUri) 111 } 112 113 const onShareAuthorDID = () => { 114 shareText(postAuthor.did) 115 } 116 117 const showExternalShareButtons = useShowExternalShareButtons() 118 const isBridgedPost = 119 !!post.record.bridgyOriginalUrl || !!post.record.fediverseId 120 const originalPostUrl = (post.record.bridgyOriginalUrl || 121 post.record.fediverseId) as string | undefined 122 123 const onOpenOriginalPost = () => { 124 if (originalPostUrl) { 125 openLink(originalPostUrl, true) 126 } 127 } 128 129 const onOpenPostInPdsls = () => { 130 openLink(`https://pdsls.dev/${post.uri}`, true) 131 } 132 133 return ( 134 <> 135 <Menu.Outer> 136 {hasSession && aa.state.access === aa.Access.Full && ( 137 <Menu.Group> 138 <Menu.ContainerItem> 139 <RecentChats postUri={postUri} /> 140 </Menu.ContainerItem> 141 <Menu.Item 142 testID="postDropdownSendViaDMBtn" 143 label={_(msg`Send via direct message`)} 144 onPress={() => { 145 ax.metric('share:press:openDmSearch', {}) 146 sendViaChatControl.open() 147 }}> 148 <Menu.ItemText> 149 <Trans>Send via direct message</Trans> 150 </Menu.ItemText> 151 <Menu.ItemIcon icon={PaperPlaneIcon} position="right" /> 152 </Menu.Item> 153 </Menu.Group> 154 )} 155 156 {showExternalShareButtons && ( 157 <Menu.Group> 158 {isBridgedPost && ( 159 <Menu.Item 160 testID="postDropdownOpenOriginalPost" 161 label={_(msg`Open original post`)} 162 onPress={onOpenOriginalPost}> 163 <Menu.ItemText> 164 <Trans>Open original post</Trans> 165 </Menu.ItemText> 166 <Menu.ItemIcon icon={ExternalIcon} position="right" /> 167 </Menu.Item> 168 )} 169 170 <Menu.Item 171 testID="postDropdownOpenInPdsls" 172 label={_(msg`Open post in PDSls`)} 173 onPress={onOpenPostInPdsls}> 174 <Menu.ItemText> 175 <Trans>Open post in PDSls</Trans> 176 </Menu.ItemText> 177 <Menu.ItemIcon icon={ExternalIcon} position="right" /> 178 </Menu.Item> 179 </Menu.Group> 180 )} 181 182 <Menu.Group> 183 <Menu.Item 184 testID="postDropdownShareBtn" 185 label={_(msg`Share via...`)} 186 onPress={onSharePost}> 187 <Menu.ItemText> 188 <Trans>Share via...</Trans> 189 </Menu.ItemText> 190 <Menu.ItemIcon icon={ArrowOutOfBoxIcon} position="right" /> 191 </Menu.Item> 192 193 <Menu.Item 194 testID="postDropdownShareBtn" 195 label={_(msg`Share via bsky.app...`)} 196 onPress={onSharePostBsky}> 197 <Menu.ItemText> 198 <Trans>Share via bsky.app...</Trans> 199 </Menu.ItemText> 200 <Menu.ItemIcon icon={ArrowOutOfBoxIcon} position="right" /> 201 </Menu.Item> 202 203 <Menu.Item 204 testID="postDropdownShareBtn" 205 label={_(msg`Copy link to post`)} 206 onPress={onCopyLink}> 207 <Menu.ItemText> 208 <Trans>Copy link to post</Trans> 209 </Menu.ItemText> 210 <Menu.ItemIcon icon={ChainLinkIcon} position="right" /> 211 </Menu.Item> 212 213 <Menu.Item 214 testID="postDropdownShareBtn" 215 label={_(msg`Copy via bsky.app`)} 216 onPress={onCopyLinkBsky}> 217 <Menu.ItemText> 218 <Trans>Copy via bsky.app</Trans> 219 </Menu.ItemText> 220 <Menu.ItemIcon icon={ChainLinkIcon} position="right" /> 221 </Menu.Item> 222 </Menu.Group> 223 224 {hideInPWI && ( 225 <Menu.Group> 226 <Menu.ContainerItem> 227 <Admonition 228 type="warning" 229 style={[a.flex_1, a.border_0, a.p_0, a.bg_transparent]}> 230 <Trans>This post is only visible to logged-in users.</Trans> 231 </Admonition> 232 </Menu.ContainerItem> 233 </Menu.Group> 234 )} 235 236 {devModeEnabled && ( 237 <Menu.Group> 238 <Menu.Item 239 testID="postAtUriShareBtn" 240 label={_(msg`Share post at:// URI`)} 241 onPress={onShareATURI}> 242 <Menu.ItemText> 243 <Trans>Share post at:// URI</Trans> 244 </Menu.ItemText> 245 <Menu.ItemIcon icon={ClipboardIcon} position="right" /> 246 </Menu.Item> 247 <Menu.Item 248 testID="postAuthorDIDShareBtn" 249 label={_(msg`Share author DID`)} 250 onPress={onShareAuthorDID}> 251 <Menu.ItemText> 252 <Trans>Share author DID</Trans> 253 </Menu.ItemText> 254 <Menu.ItemIcon icon={ClipboardIcon} position="right" /> 255 </Menu.Item> 256 </Menu.Group> 257 )} 258 </Menu.Outer> 259 260 <SendViaChatDialog 261 control={sendViaChatControl} 262 onSelectChat={onSelectChatToShareTo} 263 /> 264 </> 265 ) 266} 267ShareMenuItems = memo(ShareMenuItems) 268export {ShareMenuItems}