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