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