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