forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import {memo, useMemo} from 'react'
2import {AtUri} from '@atproto/api'
3import {msg, Trans} from '@lingui/macro'
4import {useLingui} from '@lingui/react'
5import {useNavigation} from '@react-navigation/native'
6
7import {useOpenLink} from '#/lib/hooks/useOpenLink'
8import {makeProfileLink} from '#/lib/routes/links'
9import {type NavigationProp} from '#/lib/routes/types'
10import {shareText, shareUrl} from '#/lib/sharing'
11import {toShareUrl, toShareUrlBsky} from '#/lib/strings/url-helpers'
12import {useProfileShadow} from '#/state/cache/profile-shadow'
13import {useShowExternalShareButtons} from '#/state/preferences/external-share-buttons'
14import {useSession} from '#/state/session'
15import {useBreakpoints} from '#/alf'
16import {useDialogControl} from '#/components/Dialog'
17import {EmbedDialog} from '#/components/dialogs/Embed'
18import {SendViaChatDialog} from '#/components/dms/dialogs/ShareViaChatDialog'
19import {ChainLink_Stroke2_Corner0_Rounded as ChainLinkIcon} from '#/components/icons/ChainLink'
20import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '#/components/icons/Clipboard'
21import {CodeBrackets_Stroke2_Corner0_Rounded as CodeBracketsIcon} from '#/components/icons/CodeBrackets'
22import {PaperPlane_Stroke2_Corner0_Rounded as Send} from '#/components/icons/PaperPlane'
23import {SquareArrowTopRight_Stroke2_Corner0_Rounded as ExternalIcon} from '#/components/icons/SquareArrowTopRight'
24import * as Menu from '#/components/Menu'
25import {useAgeAssurance} from '#/ageAssurance'
26import {useAnalytics} from '#/analytics'
27import {IS_WEB} from '#/env'
28import {useDevMode} from '#/storage/hooks/dev-mode'
29import {type ShareMenuItemsProps} from './ShareMenuItems.types'
30
31let ShareMenuItems = ({
32 post,
33 record,
34 timestamp,
35 onShare: onShareProp,
36}: ShareMenuItemsProps): React.ReactNode => {
37 const ax = useAnalytics()
38 const {hasSession} = useSession()
39 const {gtMobile} = useBreakpoints()
40 const {_} = useLingui()
41 const navigation = useNavigation<NavigationProp>()
42 const embedPostControl = useDialogControl()
43 const sendViaChatControl = useDialogControl()
44 const [devModeEnabled] = useDevMode()
45 const aa = useAgeAssurance()
46 const openLink = useOpenLink()
47
48 const postUri = post.uri
49 const postCid = post.cid
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 onCopyLink = () => {
64 ax.metric('share:press:copyLink', {})
65 const url = toShareUrl(href)
66 shareUrl(url)
67 onShareProp()
68 }
69
70 const onCopyLinkBsky = () => {
71 ax.metric('share:press:copyLink', {})
72 const url = toShareUrlBsky(href)
73 shareUrl(url)
74 onShareProp()
75 }
76
77 const onSelectChatToShareTo = (conversation: string) => {
78 ax.metric('share:press:dmSelected', {})
79 navigation.navigate('MessagesConversation', {
80 conversation,
81 embed: postUri,
82 })
83 }
84
85 const canEmbed = IS_WEB && gtMobile && !hideInPWI
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 if (originalPostUrl) {
103 openLink(originalPostUrl, true)
104 }
105 }
106
107 const onOpenPostInPdsls = () => {
108 openLink(`https://pdsls.dev/${post.uri}`, true)
109 }
110
111 const copyLinkItem = (
112 <Menu.Group>
113 <Menu.Item
114 testID="postDropdownShareBtn"
115 label={_(msg`Copy link to post`)}
116 onPress={onCopyLink}>
117 <Menu.ItemText>
118 <Trans>Copy link to skeet</Trans>
119 </Menu.ItemText>
120 <Menu.ItemIcon icon={ChainLinkIcon} position="right" />
121 </Menu.Item>
122 <Menu.Item
123 testID="postDropdownShareBtn"
124 label={_(msg`Copy link to post`)}
125 onPress={onCopyLinkBsky}>
126 <Menu.ItemText>
127 <Trans>Copy via bsky.app</Trans>
128 </Menu.ItemText>
129 <Menu.ItemIcon icon={ChainLinkIcon} position="right" />
130 </Menu.Item>
131 </Menu.Group>
132 )
133
134 return (
135 <>
136 <Menu.Outer>
137 {copyLinkItem}
138
139 {showExternalShareButtons && isBridgedPost && (
140 <Menu.Item
141 testID="postDropdownOpenOriginalPost"
142 label={_(msg`Open original post`)}
143 onPress={onOpenOriginalPost}>
144 <Menu.ItemText>
145 <Trans>Open original skeet</Trans>
146 </Menu.ItemText>
147 <Menu.ItemIcon icon={ExternalIcon} position="right" />
148 </Menu.Item>
149 )}
150
151 {showExternalShareButtons && (
152 <Menu.Item
153 testID="postDropdownOpenInPdsls"
154 label={_(msg`Open post in PDSls`)}
155 onPress={onOpenPostInPdsls}>
156 <Menu.ItemText>
157 <Trans>Open skeet in PDSls</Trans>
158 </Menu.ItemText>
159 <Menu.ItemIcon icon={ExternalIcon} position="right" />
160 </Menu.Item>
161 )}
162
163 {hasSession && aa.state.access === aa.Access.Full && (
164 <Menu.Item
165 testID="postDropdownSendViaDMBtn"
166 label={_(msg`Send via direct message`)}
167 onPress={() => {
168 ax.metric('share:press:openDmSearch', {})
169 sendViaChatControl.open()
170 }}>
171 <Menu.ItemText>
172 <Trans>Send via direct message</Trans>
173 </Menu.ItemText>
174 <Menu.ItemIcon icon={Send} position="right" />
175 </Menu.Item>
176 )}
177
178 {canEmbed && (
179 <Menu.Item
180 testID="postDropdownEmbedBtn"
181 label={_(msg`Embed post`)}
182 onPress={() => {
183 ax.metric('share:press:embed', {})
184 embedPostControl.open()
185 }}>
186 <Menu.ItemText>{_(msg`Embed skeet`)}</Menu.ItemText>
187 <Menu.ItemIcon icon={CodeBracketsIcon} position="right" />
188 </Menu.Item>
189 )}
190
191 {false && hideInPWI && (
192 <>
193 {hasSession && <Menu.Divider />}
194 {copyLinkItem}
195 <Menu.LabelText style={{maxWidth: 220}}>
196 <Trans>
197 Note: This skeet is only visible to logged-in users.
198 </Trans>
199 </Menu.LabelText>
200 </>
201 )}
202
203 {devModeEnabled && (
204 <>
205 <Menu.Divider />
206 <Menu.Item
207 testID="postAtUriShareBtn"
208 label={_(msg`Copy post at:// URI`)}
209 onPress={onShareATURI}>
210 <Menu.ItemText>
211 <Trans>Copy skeet at:// URI</Trans>
212 </Menu.ItemText>
213 <Menu.ItemIcon icon={ClipboardIcon} position="right" />
214 </Menu.Item>
215 <Menu.Item
216 testID="postAuthorDIDShareBtn"
217 label={_(msg`Copy author DID`)}
218 onPress={onShareAuthorDID}>
219 <Menu.ItemText>
220 <Trans>Copy author DID</Trans>
221 </Menu.ItemText>
222 <Menu.ItemIcon icon={ClipboardIcon} position="right" />
223 </Menu.Item>
224 </>
225 )}
226 </Menu.Outer>
227
228 {canEmbed && (
229 <EmbedDialog
230 control={embedPostControl}
231 postCid={postCid}
232 postUri={postUri}
233 record={record}
234 postAuthor={postAuthor}
235 timestamp={timestamp}
236 />
237 )}
238
239 <SendViaChatDialog
240 control={sendViaChatControl}
241 onSelectChat={onSelectChatToShareTo}
242 />
243 </>
244 )
245}
246ShareMenuItems = memo(ShareMenuItems)
247export {ShareMenuItems}