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

Merge pull request #87 from pyrox0/antlers/feat/PDSls

authored by isabelroses.com and committed by

GitHub a4dbb14e 3406e37c

+201 -25
+44
src/components/PostControls/ShareMenu/ShareMenuItems.tsx
··· 5 import {useLingui} from '@lingui/react' 6 import {useNavigation} from '@react-navigation/native' 7 8 import {makeProfileLink} from '#/lib/routes/links' 9 import {type NavigationProp} from '#/lib/routes/types' 10 import {shareText, shareUrl} from '#/lib/sharing' ··· 13 import {isIOS} from '#/platform/detection' 14 import {useAgeAssurance} from '#/state/ageAssurance/useAgeAssurance' 15 import {useProfileShadow} from '#/state/cache/profile-shadow' 16 import {useSession} from '#/state/session' 17 import * as Toast from '#/view/com/util/Toast' 18 import {atoms as a} from '#/alf' ··· 23 import {ChainLink_Stroke2_Corner0_Rounded as ChainLinkIcon} from '#/components/icons/ChainLink' 24 import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '#/components/icons/Clipboard' 25 import {PaperPlane_Stroke2_Corner0_Rounded as PaperPlaneIcon} from '#/components/icons/PaperPlane' 26 import * as Menu from '#/components/Menu' 27 import {useDevMode} from '#/storage/hooks/dev-mode' 28 import {RecentChats} from './RecentChats' ··· 38 const sendViaChatControl = useDialogControl() 39 const [devModeEnabled] = useDevMode() 40 const {isAgeRestricted} = useAgeAssurance() 41 42 const postUri = post.uri 43 const postAuthor = useProfileShadow(post.author) ··· 88 shareText(postAuthor.did) 89 } 90 91 return ( 92 <> 93 <Menu.Outer> ··· 107 <Trans>Send via direct message</Trans> 108 </Menu.ItemText> 109 <Menu.ItemIcon icon={PaperPlaneIcon} position="right" /> 110 </Menu.Item> 111 </Menu.Group> 112 )}
··· 5 import {useLingui} from '@lingui/react' 6 import {useNavigation} from '@react-navigation/native' 7 8 + import {useOpenLink} from '#/lib/hooks/useOpenLink' 9 import {makeProfileLink} from '#/lib/routes/links' 10 import {type NavigationProp} from '#/lib/routes/types' 11 import {shareText, shareUrl} from '#/lib/sharing' ··· 14 import {isIOS} from '#/platform/detection' 15 import {useAgeAssurance} from '#/state/ageAssurance/useAgeAssurance' 16 import {useProfileShadow} from '#/state/cache/profile-shadow' 17 + import {useShowExternalShareButtons} from '#/state/preferences/external-share-buttons' 18 import {useSession} from '#/state/session' 19 import * as Toast from '#/view/com/util/Toast' 20 import {atoms as a} from '#/alf' ··· 25 import {ChainLink_Stroke2_Corner0_Rounded as ChainLinkIcon} from '#/components/icons/ChainLink' 26 import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '#/components/icons/Clipboard' 27 import {PaperPlane_Stroke2_Corner0_Rounded as PaperPlaneIcon} from '#/components/icons/PaperPlane' 28 + import {SquareArrowTopRight_Stroke2_Corner0_Rounded as ExternalIcon} from '#/components/icons/SquareArrowTopRight' 29 import * as Menu from '#/components/Menu' 30 import {useDevMode} from '#/storage/hooks/dev-mode' 31 import {RecentChats} from './RecentChats' ··· 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) ··· 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> ··· 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 )}
+42
src/components/PostControls/ShareMenu/ShareMenuItems.web.tsx
··· 4 import {useLingui} from '@lingui/react' 5 import {useNavigation} from '@react-navigation/native' 6 7 import {makeProfileLink} from '#/lib/routes/links' 8 import {type NavigationProp} from '#/lib/routes/types' 9 import {shareText, shareUrl} from '#/lib/sharing' ··· 12 import {isWeb} from '#/platform/detection' 13 import {useAgeAssurance} from '#/state/ageAssurance/useAgeAssurance' 14 import {useProfileShadow} from '#/state/cache/profile-shadow' 15 import {useSession} from '#/state/session' 16 import {useBreakpoints} from '#/alf' 17 import {useDialogControl} from '#/components/Dialog' ··· 21 import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '#/components/icons/Clipboard' 22 import {CodeBrackets_Stroke2_Corner0_Rounded as CodeBracketsIcon} from '#/components/icons/CodeBrackets' 23 import {PaperPlane_Stroke2_Corner0_Rounded as Send} from '#/components/icons/PaperPlane' 24 import * as Menu from '#/components/Menu' 25 import {useDevMode} from '#/storage/hooks/dev-mode' 26 import {type ShareMenuItemsProps} from './ShareMenuItems.types' ··· 39 const sendViaChatControl = useDialogControl() 40 const [devModeEnabled] = useDevMode() 41 const {isAgeRestricted} = useAgeAssurance() 42 43 const postUri = post.uri 44 const postCid = post.cid ··· 80 shareText(postAuthor.did) 81 } 82 83 const copyLinkItem = ( 84 <Menu.Item 85 testID="postDropdownShareBtn" ··· 96 <> 97 <Menu.Outer> 98 {!hideInPWI && copyLinkItem} 99 100 {hasSession && !isAgeRestricted && ( 101 <Menu.Item
··· 4 import {useLingui} from '@lingui/react' 5 import {useNavigation} from '@react-navigation/native' 6 7 + import {useOpenLink} from '#/lib/hooks/useOpenLink' 8 import {makeProfileLink} from '#/lib/routes/links' 9 import {type NavigationProp} from '#/lib/routes/types' 10 import {shareText, shareUrl} from '#/lib/sharing' ··· 13 import {isWeb} from '#/platform/detection' 14 import {useAgeAssurance} from '#/state/ageAssurance/useAgeAssurance' 15 import {useProfileShadow} from '#/state/cache/profile-shadow' 16 + import {useShowExternalShareButtons} from '#/state/preferences/external-share-buttons' 17 import {useSession} from '#/state/session' 18 import {useBreakpoints} from '#/alf' 19 import {useDialogControl} from '#/components/Dialog' ··· 23 import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '#/components/icons/Clipboard' 24 import {CodeBrackets_Stroke2_Corner0_Rounded as CodeBracketsIcon} from '#/components/icons/CodeBrackets' 25 import {PaperPlane_Stroke2_Corner0_Rounded as Send} from '#/components/icons/PaperPlane' 26 + import {SquareArrowTopRight_Stroke2_Corner0_Rounded as ExternalIcon} from '#/components/icons/SquareArrowTopRight' 27 import * as Menu from '#/components/Menu' 28 import {useDevMode} from '#/storage/hooks/dev-mode' 29 import {type ShareMenuItemsProps} from './ShareMenuItems.types' ··· 42 const sendViaChatControl = useDialogControl() 43 const [devModeEnabled] = useDevMode() 44 const {isAgeRestricted} = useAgeAssurance() 45 + const openLink = useOpenLink() 46 47 const postUri = post.uri 48 const postCid = post.cid ··· 84 shareText(postAuthor.did) 85 } 86 87 + const showExternalShareButtons = useShowExternalShareButtons() 88 + const isBridgedPost = 89 + !!post.record.bridgyOriginalUrl || !!post.record.fediverseId 90 + const originalPostUrl = (post.record.bridgyOriginalUrl || 91 + post.record.fediverseId) as string | undefined 92 + 93 + const onOpenOriginalPost = () => { 94 + originalPostUrl && openLink(originalPostUrl, true) 95 + } 96 + 97 + const onOpenPostInPdsls = () => { 98 + openLink(`https://pdsls.dev/${post.uri}`, true) 99 + } 100 + 101 const copyLinkItem = ( 102 <Menu.Item 103 testID="postDropdownShareBtn" ··· 114 <> 115 <Menu.Outer> 116 {!hideInPWI && copyLinkItem} 117 + 118 + {showExternalShareButtons && isBridgedPost && ( 119 + <Menu.Item 120 + testID="postDropdownOpenOriginalPost" 121 + label={_(msg`Open original post`)} 122 + onPress={onOpenOriginalPost}> 123 + <Menu.ItemText> 124 + <Trans>Open original post</Trans> 125 + </Menu.ItemText> 126 + <Menu.ItemIcon icon={ExternalIcon} position="right" /> 127 + </Menu.Item> 128 + )} 129 + 130 + {showExternalShareButtons && ( 131 + <Menu.Item 132 + testID="postDropdownOpenInPdsls" 133 + label={_(msg`Open post in PDSls`)} 134 + onPress={onOpenPostInPdsls}> 135 + <Menu.ItemText> 136 + <Trans>Open post in PDSls</Trans> 137 + </Menu.ItemText> 138 + <Menu.ItemIcon icon={ExternalIcon} position="right" /> 139 + </Menu.Item> 140 + )} 141 142 {hasSession && !isAgeRestricted && ( 143 <Menu.Item
+30
src/screens/Settings/ExperimentalSettings.tsx
··· 19 useDirectFetchRecords, 20 useSetDirectFetchRecords, 21 } from '#/state/preferences/direct-fetch-records' 22 import * as SettingsList from '#/screens/Settings/components/SettingsList' 23 import {atoms as a} from '#/alf' 24 import {Admonition} from '#/components/Admonition' 25 import * as Toggle from '#/components/forms/Toggle' 26 import {Atom_Stroke2_Corner0_Rounded as ExperimentalIcon} from '#/components/icons/Atom' 27 import {Eye_Stroke2_Corner0_Rounded as VisibilityIcon} from '#/components/icons/Eye' 28 import {PaintRoller_Stroke2_Corner2_Rounded as PaintRollerIcon} from '#/components/icons/PaintRoller' 29 import * as Layout from '#/components/Layout' ··· 41 42 const directFetchRecords = useDirectFetchRecords() 43 const setDirectFetchRecords = useSetDirectFetchRecords() 44 45 const [gates, setGatesView] = useState(Object.fromEntries(useGatesCache())) 46 const dangerousSetGate = useDangerousSetGate() ··· 116 <Toggle.LabelText style={[a.flex_1]}> 117 <Trans> 118 TODO: Fall back to constellation api to find blocked replies 119 </Trans> 120 </Toggle.LabelText> 121 <Toggle.Platform />
··· 19 useDirectFetchRecords, 20 useSetDirectFetchRecords, 21 } from '#/state/preferences/direct-fetch-records' 22 + import { 23 + useSetShowExternalShareButtons, 24 + useShowExternalShareButtons, 25 + } from '#/state/preferences/external-share-buttons' 26 import * as SettingsList from '#/screens/Settings/components/SettingsList' 27 import {atoms as a} from '#/alf' 28 import {Admonition} from '#/components/Admonition' 29 import * as Toggle from '#/components/forms/Toggle' 30 import {Atom_Stroke2_Corner0_Rounded as ExperimentalIcon} from '#/components/icons/Atom' 31 + import {ChainLink_Stroke2_Corner0_Rounded as ChainLinkIcon} from '#/components/icons/ChainLink' 32 import {Eye_Stroke2_Corner0_Rounded as VisibilityIcon} from '#/components/icons/Eye' 33 import {PaintRoller_Stroke2_Corner2_Rounded as PaintRollerIcon} from '#/components/icons/PaintRoller' 34 import * as Layout from '#/components/Layout' ··· 46 47 const directFetchRecords = useDirectFetchRecords() 48 const setDirectFetchRecords = useSetDirectFetchRecords() 49 + 50 + const showExternalShareButtons = useShowExternalShareButtons() 51 + const setShowExternalShareButtons = useSetShowExternalShareButtons() 52 53 const [gates, setGatesView] = useState(Object.fromEntries(useGatesCache())) 54 const dangerousSetGate = useDangerousSetGate() ··· 124 <Toggle.LabelText style={[a.flex_1]}> 125 <Trans> 126 TODO: Fall back to constellation api to find blocked replies 127 + </Trans> 128 + </Toggle.LabelText> 129 + <Toggle.Platform /> 130 + </Toggle.Item> 131 + </SettingsList.Group> 132 + 133 + <SettingsList.Group contentContainerStyle={[a.gap_sm]}> 134 + <SettingsList.ItemIcon icon={ChainLinkIcon} /> 135 + <SettingsList.ItemText> 136 + <Trans>Bridging and Fediverse</Trans> 137 + </SettingsList.ItemText> 138 + <Toggle.Item 139 + name="external_share_buttons" 140 + label={_( 141 + msg`Show "Open original post" and "Open post in PDSls" buttons`, 142 + )} 143 + value={showExternalShareButtons} 144 + onChange={value => setShowExternalShareButtons(value)} 145 + style={[a.w_full]}> 146 + <Toggle.LabelText style={[a.flex_1]}> 147 + <Trans> 148 + Show "Open original post" and "Open post in PDSls" buttons 149 </Trans> 150 </Toggle.LabelText> 151 <Toggle.Platform />
+3
src/state/persisted/schema.ts
··· 130 directFetchRecords: z.boolean().optional(), 131 unfollowConfirm: z.boolean().optional(), 132 133 /** @deprecated */ 134 mutedThreads: z.array(z.string()), 135 trendingDisabled: z.boolean().optional(), ··· 187 constellationEnabled: false, 188 directFetchRecords: false, 189 unfollowConfirm: false, 190 } 191 192 export function tryParse(rawData: string): Schema | undefined {
··· 130 directFetchRecords: z.boolean().optional(), 131 unfollowConfirm: z.boolean().optional(), 132 133 + showExternalShareButtons: z.boolean().optional(), 134 + 135 /** @deprecated */ 136 mutedThreads: z.array(z.string()), 137 trendingDisabled: z.boolean().optional(), ··· 189 constellationEnabled: false, 190 directFetchRecords: false, 191 unfollowConfirm: false, 192 + showExternalShareButtons: false, 193 } 194 195 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
··· 6 import {Provider as DirectFetchRecordsProvider} from './direct-fetch-records' 7 import {Provider as DisableHapticsProvider} from './disable-haptics' 8 import {Provider as ExternalEmbedsProvider} from './external-embeds-prefs' 9 import {Provider as GoLinksProvider} from './go-links-enabled' 10 import {Provider as HiddenPostsProvider} from './hidden-posts' 11 import {Provider as InAppBrowserProvider} from './in-app-browser' ··· 36 return ( 37 <LanguagesProvider> 38 <AltTextRequiredProvider> 39 - <GoLinksProvider> 40 - <DirectFetchRecordsProvider> 41 - <ConstellationProvider> 42 - <LargeAltBadgeProvider> 43 - <ExternalEmbedsProvider> 44 - <HiddenPostsProvider> 45 - <InAppBrowserProvider> 46 - <DisableHapticsProvider> 47 - <AutoplayProvider> 48 - <UsedStarterPacksProvider> 49 - <SubtitlesProvider> 50 - <TrendingSettingsProvider> 51 - <KawaiiProvider>{children}</KawaiiProvider> 52 - </TrendingSettingsProvider> 53 - </SubtitlesProvider> 54 - </UsedStarterPacksProvider> 55 - </AutoplayProvider> 56 - </DisableHapticsProvider> 57 - </InAppBrowserProvider> 58 - </HiddenPostsProvider> 59 - </ExternalEmbedsProvider> 60 - </LargeAltBadgeProvider> 61 - </ConstellationProvider> 62 - </DirectFetchRecordsProvider> 63 - </GoLinksProvider> 64 </AltTextRequiredProvider> 65 </LanguagesProvider> 66 )
··· 6 import {Provider as DirectFetchRecordsProvider} from './direct-fetch-records' 7 import {Provider as DisableHapticsProvider} from './disable-haptics' 8 import {Provider as ExternalEmbedsProvider} from './external-embeds-prefs' 9 + import {Provider as ExternalShareButtonsProvider} from './external-share-buttons' 10 import {Provider as GoLinksProvider} from './go-links-enabled' 11 import {Provider as HiddenPostsProvider} from './hidden-posts' 12 import {Provider as InAppBrowserProvider} from './in-app-browser' ··· 37 return ( 38 <LanguagesProvider> 39 <AltTextRequiredProvider> 40 + <ExternalShareButtonsProvider> 41 + <GoLinksProvider> 42 + <DirectFetchRecordsProvider> 43 + <ConstellationProvider> 44 + <LargeAltBadgeProvider> 45 + <ExternalEmbedsProvider> 46 + <HiddenPostsProvider> 47 + <InAppBrowserProvider> 48 + <DisableHapticsProvider> 49 + <AutoplayProvider> 50 + <UsedStarterPacksProvider> 51 + <SubtitlesProvider> 52 + <TrendingSettingsProvider> 53 + <KawaiiProvider>{children}</KawaiiProvider> 54 + </TrendingSettingsProvider> 55 + </SubtitlesProvider> 56 + </UsedStarterPacksProvider> 57 + </AutoplayProvider> 58 + </DisableHapticsProvider> 59 + </InAppBrowserProvider> 60 + </HiddenPostsProvider> 61 + </ExternalEmbedsProvider> 62 + </LargeAltBadgeProvider> 63 + </ConstellationProvider> 64 + </DirectFetchRecordsProvider> 65 + </GoLinksProvider> 66 + </ExternalShareButtonsProvider> 67 </AltTextRequiredProvider> 68 </LanguagesProvider> 69 )