From 8eaa538673f957375fab85951a2d33a1514cc64c Mon Sep 17 00:00:00 2001 From: Hailey Date: Thu, 20 Jun 2024 11:31:59 -0700 Subject: [PATCH] Add link shortening to starter pack link sharing (#4581) --- src/components/StarterPack/QrCode.tsx | 10 +- src/components/StarterPack/QrCodeDialog.tsx | 93 +++++++++++++------ src/screens/StarterPack/StarterPackScreen.tsx | 81 +++++++++++----- src/state/queries/shorten-link.ts | 23 +++++ 4 files changed, 150 insertions(+), 57 deletions(-) create mode 100644 src/state/queries/shorten-link.ts diff --git a/src/components/StarterPack/QrCode.tsx b/src/components/StarterPack/QrCode.tsx index 6d348194d6..08ee03d623 100644 --- a/src/components/StarterPack/QrCode.tsx +++ b/src/components/StarterPack/QrCode.tsx @@ -5,7 +5,6 @@ import ViewShot from 'react-native-view-shot' import {AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api' import {Trans} from '@lingui/macro' -import {makeStarterPackLink} from 'lib/routes/links' import {isWeb} from 'platform/detection' import {Logo} from 'view/icons/Logo' import {Logotype} from 'view/icons/Logotype' @@ -16,10 +15,11 @@ import {Text} from '#/components/Typography' interface Props { starterPack: AppBskyGraphDefs.StarterPackView + link: string } export const QrCode = React.forwardRef(function QrCode( - {starterPack}, + {starterPack, link}, ref, ) { const {record} = starterPack @@ -56,7 +56,7 @@ export const QrCode = React.forwardRef(function QrCode( Join the conversation - + @@ -79,12 +79,12 @@ export const QrCode = React.forwardRef(function QrCode( ) }) -export function QrCodeInner({url}: {url: string}) { +export function QrCodeInner({link}: {link: string}) { const t = useTheme() return ( + + + ) +} + +function Inner({ + starterPack, + control, +}: { + starterPack: AppBskyGraphDefs.StarterPackView + control: DialogControlProps }) { const {_} = useLingui() const [isProcessing, setIsProcessing] = React.useState(false) + const [link, setLink] = React.useState() + const shortenLink = useShortenLink() const ref = React.useRef(null) + React.useEffect(() => { + if (link) return + ;(async () => { + const rkey = new AtUri(starterPack.uri).rkey + const res = await shortenLink( + makeStarterPackLink(starterPack.creator.did, rkey), + ) + setLink(res.url) + })() + }, [link, shortenLink, starterPack.creator.did, starterPack.uri]) + const getCanvas = (base64: string): Promise => { return new Promise(resolve => { const image = new Image() @@ -149,42 +178,50 @@ export function QrCodeDialog({ } return ( - + <> - - {isProcessing ? ( - + {!link ? ( + ) : ( - - - - + <> + + {isProcessing ? ( + + + + ) : ( + + + + + )} + )} - + ) } diff --git a/src/screens/StarterPack/StarterPackScreen.tsx b/src/screens/StarterPack/StarterPackScreen.tsx index 085b39daad..d645d9db47 100644 --- a/src/screens/StarterPack/StarterPackScreen.tsx +++ b/src/screens/StarterPack/StarterPackScreen.tsx @@ -1,6 +1,10 @@ import React from 'react' import {View} from 'react-native' -import {AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api' +import { + AppBskyGraphDefs, + AppBskyGraphStarterpack, + ModerationOpts, +} from '@atproto/api' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' @@ -20,6 +24,7 @@ import {isWeb} from 'platform/detection' import {useModerationOpts} from 'state/preferences/moderation-opts' import {RQKEY} from 'state/queries/list-members' import {useResolveDidQuery} from 'state/queries/resolve-uri' +import {useShortenLink} from 'state/queries/shorten-link' import {useStarterPackQuery} from 'state/queries/starter-packs' import {useAgent, useSession} from 'state/session' import * as Toast from '#/view/com/util/Toast' @@ -83,11 +88,42 @@ export function StarterPackScreen({route}: StarterPackScreeProps) { ) } + return ( + + ) +} + +function StarterPackScreenInner({ + starterPack, + routeParams, + moderationOpts, +}: { + starterPack: AppBskyGraphDefs.StarterPackView + routeParams: StarterPackScreeProps['route']['params'] + moderationOpts: ModerationOpts +}) { + const shortenLink = useShortenLink() + const tabs = [ ...(starterPack.list ? ['People'] : []), ...(starterPack.feeds?.length ? ['Feeds'] : []), ] + const onShareLink = async () => { + const res = await shortenLink( + makeStarterPackLink(starterPack.creator.did, routeParams.rkey), + ) + shareUrl(res.url) + logEvent('starterPack:share', { + starterPack: starterPack.uri, + shareType: 'link', + }) + } + return ( @@ -95,7 +131,11 @@ export function StarterPackScreen({route}: StarterPackScreeProps) { items={tabs} isHeaderReady={true} renderHeader={() => ( -
+
)}> {starterPack.list != null ? ({headerHeight, scrollElRef}) => ( @@ -131,9 +171,11 @@ export function StarterPackScreen({route}: StarterPackScreeProps) { function Header({ starterPack, routeParams, + onShareLink, }: { starterPack: AppBskyGraphDefs.StarterPackView routeParams: StarterPackScreeProps['route']['params'] + onShareLink: () => void }) { const {_} = useLingui() const t = useTheme() @@ -195,8 +237,8 @@ function Header({ {isOwn ? ( ) : ( )} - + {record.description || joinedAllTimeCount >= 25 ? ( @@ -247,9 +293,11 @@ function Header({ function OverflowMenu({ starterPack, routeParams, + onShareLink, }: { starterPack: AppBskyGraphDefs.StarterPackView routeParams: StarterPackScreeProps['route']['params'] + onShareLink: () => void }) { const t = useTheme() const {_} = useLingui() @@ -259,6 +307,7 @@ function OverflowMenu({ const reportDialogControl = useReportDialogControl() const deleteDialogControl = useDialogControl() const navigation = useNavigation() + const { mutate: deleteStarterPack, isPending: isDeletePending, @@ -342,15 +391,7 @@ function OverflowMenu({ { - logEvent('starterPack:share', { - starterPack: starterPack.uri, - shareType: 'link', - }) - shareUrl( - makeStarterPackLink(routeParams.name, routeParams.rkey), - ) - }}> + onPress={onShareLink}> Share link @@ -439,10 +480,10 @@ function OverflowMenu({ function OwnerShareMenu({ starterPack, - routeParams, + onShareLink, }: { starterPack: AppBskyGraphDefs.StarterPackView - routeParams: StarterPackScreeProps['route']['params'] + onShareLink: () => void }) { const {_} = useLingui() const qrCodeDialogControl = useDialogControl() @@ -471,15 +512,7 @@ function OwnerShareMenu({ { - logEvent('starterPack:share', { - starterPack: starterPack.uri, - shareType: 'link', - }) - shareUrl( - makeStarterPackLink(routeParams.name, routeParams.rkey), - ) - }}> + onPress={onShareLink}> Share link diff --git a/src/state/queries/shorten-link.ts b/src/state/queries/shorten-link.ts new file mode 100644 index 0000000000..76c63c3569 --- /dev/null +++ b/src/state/queries/shorten-link.ts @@ -0,0 +1,23 @@ +import {logger} from '#/logger' + +export function useShortenLink() { + return async (inputUrl: string): Promise<{url: string}> => { + const url = new URL(inputUrl) + const res = await fetch('https://go.bsky.app/link', { + method: 'POST', + body: JSON.stringify({ + path: url.pathname, + }), + headers: { + 'Content-Type': 'application/json', + }, + }) + + if (!res.ok) { + logger.error('Failed to shorten link', {safeMessage: res.status}) + return {url: inputUrl} + } + + return res.json() + } +}