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