import {memo, useMemo} from 'react' import * as ExpoClipboard from 'expo-clipboard' import {AtUri} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useNavigation} from '@react-navigation/native' import {useOpenLink} from '#/lib/hooks/useOpenLink' import {makeProfileLink} from '#/lib/routes/links' import {type NavigationProp} from '#/lib/routes/types' import {shareText, shareUrl} from '#/lib/sharing' import {getTerminology, TERMINOLOGY} from '#/lib/strings/terminology' import {toShareUrl, toShareUrlBsky} from '#/lib/strings/url-helpers' import {logger} from '#/logger' import {isIOS} from '#/platform/detection' import {useProfileShadow} from '#/state/cache/profile-shadow' import {useTerminologyPreference} from '#/state/preferences' import {useShowExternalShareButtons} from '#/state/preferences/external-share-buttons' import {useSession} from '#/state/session' import * as Toast from '#/view/com/util/Toast' import {atoms as a} from '#/alf' import {Admonition} from '#/components/Admonition' import {useDialogControl} from '#/components/Dialog' import {SendViaChatDialog} from '#/components/dms/dialogs/ShareViaChatDialog' import {ArrowOutOfBoxModified_Stroke2_Corner2_Rounded as ArrowOutOfBoxIcon} from '#/components/icons/ArrowOutOfBox' import {ChainLink_Stroke2_Corner0_Rounded as ChainLinkIcon} from '#/components/icons/ChainLink' import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '#/components/icons/Clipboard' import {PaperPlane_Stroke2_Corner0_Rounded as PaperPlaneIcon} from '#/components/icons/PaperPlane' import {SquareArrowTopRight_Stroke2_Corner0_Rounded as ExternalIcon} from '#/components/icons/SquareArrowTopRight' import * as Menu from '#/components/Menu' import {useAgeAssurance} from '#/ageAssurance' import {useDevMode} from '#/storage/hooks/dev-mode' import {RecentChats} from './RecentChats' import {type ShareMenuItemsProps} from './ShareMenuItems.types' let ShareMenuItems = ({ post, onShare: onShareProp, }: ShareMenuItemsProps): React.ReactNode => { const {hasSession} = useSession() const {_} = useLingui() const terminologyPreference = useTerminologyPreference() const navigation = useNavigation() const sendViaChatControl = useDialogControl() const [devModeEnabled] = useDevMode() const aa = useAgeAssurance() const openLink = useOpenLink() const postUri = post.uri const postAuthor = useProfileShadow(post.author) const href = useMemo(() => { const urip = new AtUri(postUri) return makeProfileLink(postAuthor, 'post', urip.rkey) }, [postUri, postAuthor]) const hideInPWI = useMemo(() => { return !!postAuthor.labels?.find( label => label.val === '!no-unauthenticated', ) }, [postAuthor]) const onSharePost = () => { logger.metric('share:press:nativeShare', {}, {statsig: true}) const url = toShareUrl(href) shareUrl(url) onShareProp() } const onSharePostBsky = () => { logger.metric('share:press:nativeShare', {}, {statsig: true}) const url = toShareUrlBsky(href) shareUrl(url) onShareProp() } const onCopyLink = async () => { logger.metric('share:press:copyLink', {}, {statsig: true}) const url = toShareUrl(href) if (isIOS) { // iOS only await ExpoClipboard.setUrlAsync(url) } else { await ExpoClipboard.setStringAsync(url) } Toast.show(_(msg`Copied to clipboard`), 'clipboard-check') onShareProp() } const onCopyLinkBsky = async () => { logger.metric('share:press:copyLink', {}, {statsig: true}) const url = toShareUrlBsky(href) if (isIOS) { // iOS only await ExpoClipboard.setUrlAsync(url) } else { await ExpoClipboard.setStringAsync(url) } Toast.show(_(msg`Copied to clipboard`), 'clipboard-check') onShareProp() } const onSelectChatToShareTo = (conversation: string) => { navigation.navigate('MessagesConversation', { conversation, embed: postUri, }) } const onShareATURI = () => { shareText(postUri) } const onShareAuthorDID = () => { shareText(postAuthor.did) } const showExternalShareButtons = useShowExternalShareButtons() const isBridgedPost = !!post.record.bridgyOriginalUrl || !!post.record.fediverseId const originalPostUrl = (post.record.bridgyOriginalUrl || post.record.fediverseId) as string | undefined const onOpenOriginalPost = () => { originalPostUrl && openLink(originalPostUrl, true) } const onOpenPostInPdsls = () => { openLink(`https://pdsls.dev/${post.uri}`, true) } return ( <> {hasSession && aa.state.access === aa.Access.Full && ( { logger.metric('share:press:openDmSearch', {}, {statsig: true}) sendViaChatControl.open() }}> Send via direct message )} {showExternalShareButtons && ( {isBridgedPost && ( {_(getTerminology(terminologyPreference, { skeet: msg`Open original skeet`, post: msg`Open original post`, spell: msg`Open original spell`, }))} )} {_(getTerminology(terminologyPreference, { skeet: msg`Open skeet in PDSls`, post: msg`Open post in PDSls`, spell: msg`Open spell in PDSls`, }))} )} Share via... Share via bsky.app... {_(getTerminology(terminologyPreference, { skeet: msg`Copy link to skeet`, post: msg`Copy link to post`, spell: msg`Copy link to spell`, }))} Copy via bsky.app {hideInPWI && ( {_(getTerminology(terminologyPreference, { skeet: msg`This skeet is only visible to logged-in users.`, post: msg`This post is only visible to logged-in users.`, spell: msg`This spell is only visible to logged-in users.`, }))} )} {devModeEnabled && ( {_(getTerminology(terminologyPreference, { skeet: msg`Share skeet at:// URI`, post: msg`Share post at:// URI`, spell: msg`Share spell at:// URI`, }))} Share author DID )} ) } ShareMenuItems = memo(ShareMenuItems) export {ShareMenuItems}