Bluesky app fork with some witchin' additions 💫

antlers PDSls & bridged share buttons

xan.lol c416f4af 0cb096c0

verified
+201 -25
+44
src/components/PostControls/ShareMenu/ShareMenuItems.tsx
··· 5 5 import {useLingui} from '@lingui/react' 6 6 import {useNavigation} from '@react-navigation/native' 7 7 8 + import {useOpenLink} from '#/lib/hooks/useOpenLink' 8 9 import {makeProfileLink} from '#/lib/routes/links' 9 10 import {type NavigationProp} from '#/lib/routes/types' 10 11 import {shareText, shareUrl} from '#/lib/sharing' ··· 13 14 import {isIOS} from '#/platform/detection' 14 15 import {useAgeAssurance} from '#/state/ageAssurance/useAgeAssurance' 15 16 import {useProfileShadow} from '#/state/cache/profile-shadow' 17 + import {useShowExternalShareButtons} from '#/state/preferences/external-share-buttons' 16 18 import {useSession} from '#/state/session' 17 19 import * as Toast from '#/view/com/util/Toast' 18 20 import {atoms as a} from '#/alf' ··· 23 25 import {ChainLink_Stroke2_Corner0_Rounded as ChainLinkIcon} from '#/components/icons/ChainLink' 24 26 import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '#/components/icons/Clipboard' 25 27 import {PaperPlane_Stroke2_Corner0_Rounded as PaperPlaneIcon} from '#/components/icons/PaperPlane' 28 + import {SquareArrowTopRight_Stroke2_Corner0_Rounded as ExternalIcon} from '#/components/icons/SquareArrowTopRight' 26 29 import * as Menu from '#/components/Menu' 27 30 import {useDevMode} from '#/storage/hooks/dev-mode' 28 31 import {RecentChats} from './RecentChats' ··· 38 41 const sendViaChatControl = useDialogControl() 39 42 const [devModeEnabled] = useDevMode() 40 43 const {isAgeRestricted} = useAgeAssurance() 44 + const openLink = useOpenLink() 41 45 42 46 const postUri = post.uri 43 47 const postAuthor = useProfileShadow(post.author) ··· 108 112 shareText(postAuthor.did) 109 113 } 110 114 115 + const showExternalShareButtons = useShowExternalShareButtons() 116 + const isBridgedPost = 117 + !!post.record.bridgyOriginalUrl || !!post.record.fediverseId 118 + const originalPostUrl = (post.record.bridgyOriginalUrl || 119 + post.record.fediverseId) as string | undefined 120 + 121 + const onOpenOriginalPost = () => { 122 + originalPostUrl && openLink(originalPostUrl, true) 123 + } 124 + 125 + const onOpenPostInPdsls = () => { 126 + openLink(`https://pdsls.dev/${post.uri}`, true) 127 + } 128 + 111 129 return ( 112 130 <> 113 131 <Menu.Outer> ··· 127 145 <Trans>Send via direct message</Trans> 128 146 </Menu.ItemText> 129 147 <Menu.ItemIcon icon={PaperPlaneIcon} position="right" /> 148 + </Menu.Item> 149 + </Menu.Group> 150 + )} 151 + 152 + {showExternalShareButtons && ( 153 + <Menu.Group> 154 + {isBridgedPost && ( 155 + <Menu.Item 156 + testID="postDropdownOpenOriginalPost" 157 + label={_(msg`Open original post`)} 158 + onPress={onOpenOriginalPost}> 159 + <Menu.ItemText> 160 + <Trans>Open original post</Trans> 161 + </Menu.ItemText> 162 + <Menu.ItemIcon icon={ExternalIcon} position="right" /> 163 + </Menu.Item> 164 + )} 165 + 166 + <Menu.Item 167 + testID="postDropdownOpenInPdsls" 168 + label={_(msg`Open post in PDSls`)} 169 + onPress={onOpenPostInPdsls}> 170 + <Menu.ItemText> 171 + <Trans>Open post in PDSls</Trans> 172 + </Menu.ItemText> 173 + <Menu.ItemIcon icon={ExternalIcon} position="right" /> 130 174 </Menu.Item> 131 175 </Menu.Group> 132 176 )}
+42
src/components/PostControls/ShareMenu/ShareMenuItems.web.tsx
··· 4 4 import {useLingui} from '@lingui/react' 5 5 import {useNavigation} from '@react-navigation/native' 6 6 7 + import {useOpenLink} from '#/lib/hooks/useOpenLink' 7 8 import {makeProfileLink} from '#/lib/routes/links' 8 9 import {type NavigationProp} from '#/lib/routes/types' 9 10 import {shareText, shareUrl} from '#/lib/sharing' ··· 12 13 import {isWeb} from '#/platform/detection' 13 14 import {useAgeAssurance} from '#/state/ageAssurance/useAgeAssurance' 14 15 import {useProfileShadow} from '#/state/cache/profile-shadow' 16 + import {useShowExternalShareButtons} from '#/state/preferences/external-share-buttons' 15 17 import {useSession} from '#/state/session' 16 18 import {useBreakpoints} from '#/alf' 17 19 import {useDialogControl} from '#/components/Dialog' ··· 21 23 import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '#/components/icons/Clipboard' 22 24 import {CodeBrackets_Stroke2_Corner0_Rounded as CodeBracketsIcon} from '#/components/icons/CodeBrackets' 23 25 import {PaperPlane_Stroke2_Corner0_Rounded as Send} from '#/components/icons/PaperPlane' 26 + import {SquareArrowTopRight_Stroke2_Corner0_Rounded as ExternalIcon} from '#/components/icons/SquareArrowTopRight' 24 27 import * as Menu from '#/components/Menu' 25 28 import {useDevMode} from '#/storage/hooks/dev-mode' 26 29 import {type ShareMenuItemsProps} from './ShareMenuItems.types' ··· 39 42 const sendViaChatControl = useDialogControl() 40 43 const [devModeEnabled] = useDevMode() 41 44 const {isAgeRestricted} = useAgeAssurance() 45 + const openLink = useOpenLink() 42 46 43 47 const postUri = post.uri 44 48 const postCid = post.cid ··· 87 91 shareText(postAuthor.did) 88 92 } 89 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 + 90 108 const copyLinkItem = ( 91 109 <Menu.Group> 92 110 <Menu.Item ··· 114 132 <> 115 133 <Menu.Outer> 116 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 post</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 post in PDSls</Trans> 155 + </Menu.ItemText> 156 + <Menu.ItemIcon icon={ExternalIcon} position="right" /> 157 + </Menu.Item> 158 + )} 117 159 118 160 {hasSession && !isAgeRestricted && ( 119 161 <Menu.Item
+30
src/screens/Settings/DeerSettings.tsx
··· 87 87 useShowLinkInHandle, 88 88 } from '#/state/preferences/show-link-in-handle.tsx' 89 89 import {useProfilesQuery} from '#/state/queries/profile' 90 + import { 91 + useSetShowExternalShareButtons, 92 + useShowExternalShareButtons, 93 + } from '#/state/preferences/external-share-buttons' 90 94 import * as SettingsList from '#/screens/Settings/components/SettingsList' 91 95 import {atoms as a, useBreakpoints} from '#/alf' 92 96 import {Admonition} from '#/components/Admonition' ··· 94 98 import * as Dialog from '#/components/Dialog' 95 99 import * as Toggle from '#/components/forms/Toggle' 96 100 import {Atom_Stroke2_Corner0_Rounded as DeerIcon} from '#/components/icons/Atom' 101 + import {ChainLink_Stroke2_Corner0_Rounded as ChainLinkIcon} from '#/components/icons/ChainLink' 97 102 import {Eye_Stroke2_Corner0_Rounded as VisibilityIcon} from '#/components/icons/Eye' 98 103 import {Lab_Stroke2_Corner0_Rounded as BeakerIcon} from '#/components/icons/Lab' 99 104 import {PaintRoller_Stroke2_Corner2_Rounded as PaintRollerIcon} from '#/components/icons/PaintRoller' ··· 243 248 const directFetchRecords = useDirectFetchRecords() 244 249 const setDirectFetchRecords = useSetDirectFetchRecords() 245 250 251 + const showExternalShareButtons = useShowExternalShareButtons() 252 + const setShowExternalShareButtons = useSetShowExternalShareButtons() 253 + 246 254 const noAppLabelers = useNoAppLabelers() 247 255 const setNoAppLabelers = useSetNoAppLabelers() 248 256 ··· 348 356 <Trans> 349 357 Fetch records directly from PDS to see contents of blocked and 350 358 detatched quotes 359 + </Trans> 360 + </Toggle.LabelText> 361 + <Toggle.Platform /> 362 + </Toggle.Item> 363 + </SettingsList.Group> 364 + 365 + <SettingsList.Group contentContainerStyle={[a.gap_sm]}> 366 + <SettingsList.ItemIcon icon={ChainLinkIcon} /> 367 + <SettingsList.ItemText> 368 + <Trans>Bridging and Fediverse</Trans> 369 + </SettingsList.ItemText> 370 + <Toggle.Item 371 + name="external_share_buttons" 372 + label={_( 373 + msg`Show "Open original post" and "Open post in PDSls" buttons`, 374 + )} 375 + value={showExternalShareButtons} 376 + onChange={value => setShowExternalShareButtons(value)} 377 + style={[a.w_full]}> 378 + <Toggle.LabelText style={[a.flex_1]}> 379 + <Trans> 380 + Show "Open original post" and "Open post in PDSls" buttons 351 381 </Trans> 352 382 </Toggle.LabelText> 353 383 <Toggle.Platform />
+3
src/state/persisted/schema.ts
··· 149 149 .optional(), 150 150 highQualityImages: z.boolean().optional(), 151 151 152 + showExternalShareButtons: z.boolean().optional(), 153 + 152 154 /** @deprecated */ 153 155 mutedThreads: z.array(z.string()), 154 156 trendingDisabled: z.boolean().optional(), ··· 240 242 ], 241 243 }, 242 244 highQualityImages: false, 245 + showExternalShareButtons: false, 243 246 } 244 247 245 248 export function tryParse(rawData: string): Schema | undefined {
+54
src/state/preferences/external-share-buttons.tsx
··· 1 + import React from 'react' 2 + 3 + import * as persisted from '#/state/persisted' 4 + 5 + type StateContext = persisted.Schema['showExternalShareButtons'] 6 + type SetContext = (v: persisted.Schema['showExternalShareButtons']) => void 7 + 8 + const stateContext = React.createContext<StateContext>( 9 + persisted.defaults.showExternalShareButtons, 10 + ) 11 + const setContext = React.createContext<SetContext>( 12 + (_: persisted.Schema['showExternalShareButtons']) => {}, 13 + ) 14 + 15 + export function Provider({children}: React.PropsWithChildren<{}>) { 16 + const [state, setState] = React.useState( 17 + persisted.get('showExternalShareButtons'), 18 + ) 19 + 20 + const setStateWrapped = React.useCallback( 21 + ( 22 + showExternalShareButtons: persisted.Schema['showExternalShareButtons'], 23 + ) => { 24 + setState(showExternalShareButtons) 25 + persisted.write('showExternalShareButtons', showExternalShareButtons) 26 + }, 27 + [setState], 28 + ) 29 + 30 + React.useEffect(() => { 31 + return persisted.onUpdate( 32 + 'showExternalShareButtons', 33 + nextShowExternalShareButtons => { 34 + setState(nextShowExternalShareButtons) 35 + }, 36 + ) 37 + }, [setStateWrapped]) 38 + 39 + return ( 40 + <stateContext.Provider value={state}> 41 + <setContext.Provider value={setStateWrapped}> 42 + {children} 43 + </setContext.Provider> 44 + </stateContext.Provider> 45 + ) 46 + } 47 + 48 + export function useShowExternalShareButtons() { 49 + return React.useContext(stateContext) 50 + } 51 + 52 + export function useSetShowExternalShareButtons() { 53 + return React.useContext(setContext) 54 + }
+28 -25
src/state/preferences/index.tsx
··· 14 14 import {Provider as DisableSavesMetricsProvider} from './disable-saves-metrics' 15 15 import {Provider as DisableViaRepostNotificationProvider} from './disable-via-repost-notification' 16 16 import {Provider as ExternalEmbedsProvider} from './external-embeds-prefs' 17 + import {Provider as ExternalShareButtonsProvider} from './external-share-buttons' 17 18 import {Provider as GoLinksProvider} from './go-links-enabled' 18 19 import {Provider as HiddenPostsProvider} from './hidden-posts' 19 20 import {Provider as HideFeedsPromoTabProvider} from './hide-feeds-promo-tab' ··· 55 56 return ( 56 57 <LanguagesProvider> 57 58 <AltTextRequiredProvider> 58 - <GoLinksProvider> 59 + <ExternalShareButtonsProvider> 60 + <GoLinksProvider> 59 61 <NoAppLabelersProvider> 60 - <DirectFetchRecordsProvider> 61 - <ConstellationProvider> 62 + <DirectFetchRecordsProvider> 63 + <ConstellationProvider> 62 64 <ConstellationInstanceProvider> 63 65 <DeerVerificationProvider> 64 66 <NoDiscoverProvider> 65 67 <ShowLinkInHandleProvider> 66 - <LargeAltBadgeProvider> 67 - <ExternalEmbedsProvider> 68 - <HiddenPostsProvider> 68 + <LargeAltBadgeProvider> 69 + <ExternalEmbedsProvider> 70 + <HiddenPostsProvider> 69 71 <HighQualityImagesProvider> 70 - <InAppBrowserProvider> 71 - <DisableHapticsProvider> 72 - <AutoplayProvider> 73 - <UsedStarterPacksProvider> 74 - <SubtitlesProvider> 75 - <TrendingSettingsProvider> 76 - <RepostCarouselProvider> 72 + <InAppBrowserProvider> 73 + <DisableHapticsProvider> 74 + <AutoplayProvider> 75 + <UsedStarterPacksProvider> 76 + <SubtitlesProvider> 77 + <TrendingSettingsProvider> 78 + <RepostCarouselProvider> 77 79 <KawaiiProvider> 78 80 <HideFeedsPromoTabProvider> 79 81 <DisableViaRepostNotificationProvider> ··· 94 96 </HideFeedsPromoTabProvider> 95 97 </KawaiiProvider> 96 98 </RepostCarouselProvider> 97 - </TrendingSettingsProvider> 98 - </SubtitlesProvider> 99 - </UsedStarterPacksProvider> 100 - </AutoplayProvider> 101 - </DisableHapticsProvider> 102 - </InAppBrowserProvider> 99 + </TrendingSettingsProvider> 100 + </SubtitlesProvider> 101 + </UsedStarterPacksProvider> 102 + </AutoplayProvider> 103 + </DisableHapticsProvider> 104 + </InAppBrowserProvider> 103 105 </HighQualityImagesProvider> 104 - </HiddenPostsProvider> 105 - </ExternalEmbedsProvider> 106 - </LargeAltBadgeProvider> 106 + </HiddenPostsProvider> 107 + </ExternalEmbedsProvider> 108 + </LargeAltBadgeProvider> 107 109 </ShowLinkInHandleProvider> 108 110 </NoDiscoverProvider> 109 111 </DeerVerificationProvider> 110 112 </ConstellationInstanceProvider> 111 - </ConstellationProvider> 112 - </DirectFetchRecordsProvider> 113 + </ConstellationProvider> 114 + </DirectFetchRecordsProvider> 113 115 </NoAppLabelersProvider> 114 - </GoLinksProvider> 116 + </GoLinksProvider> 117 + </ExternalShareButtonsProvider> 115 118 </AltTextRequiredProvider> 116 119 </LanguagesProvider> 117 120 )