From 91c4aa7c2dc598dd5e2c828e44c0d2c94cf0967d Mon Sep 17 00:00:00 2001 From: Hailey Date: Thu, 27 Jun 2024 19:35:20 -0700 Subject: [PATCH] Handle pressing all go.bsky.app links in-app w/ resolution (#4680) --- src/Navigation.tsx | 12 ++- src/lib/link-meta/resolve-short-link.ts | 10 ++- src/lib/routes/types.ts | 2 + src/lib/strings/url-helpers.ts | 17 +++- src/routes.ts | 1 + .../StarterPack/StarterPackLandingScreen.tsx | 37 +++++++-- src/screens/StarterPack/StarterPackScreen.tsx | 80 +++++++++++++++++-- src/state/queries/resolve-short-link.ts | 24 ++++++ src/state/shell/logged-out.tsx | 20 +++++ 9 files changed, 186 insertions(+), 17 deletions(-) create mode 100644 src/state/queries/resolve-short-link.ts diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 5cb4f410..4ecf3fff 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -43,7 +43,10 @@ import HashtagScreen from '#/screens/Hashtag' import {ModerationScreen} from '#/screens/Moderation' import {ProfileKnownFollowersScreen} from '#/screens/Profile/KnownFollowers' import {ProfileLabelerLikedByScreen} from '#/screens/Profile/ProfileLabelerLikedBy' -import {StarterPackScreen} from '#/screens/StarterPack/StarterPackScreen' +import { + StarterPackScreen, + StarterPackScreenShort, +} from '#/screens/StarterPack/StarterPackScreen' import {Wizard} from '#/screens/StarterPack/Wizard' import {init as initAnalytics} from './lib/analytics/analytics' import {useWebScrollRestoration} from './lib/hooks/useWebScrollRestoration' @@ -322,7 +325,12 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) { StarterPackScreen} - options={{title: title(msg`Starter Pack`), requireAuth: true}} + options={{title: title(msg`Starter Pack`)}} + /> + StarterPackScreenShort} + options={{title: title(msg`Starter Pack`)}} /> } @@ -112,9 +117,6 @@ function LandingScreenLoaded({ const listItemsCount = starterPack.list?.listItemCount ?? 0 const onContinue = () => { - setActiveStarterPack({ - uri: starterPack.uri, - }) setScreenState(LoggedOutScreenState.S_CreateAccount) } @@ -166,6 +168,31 @@ function LandingScreenLoaded({ paddingTop: 100, }, ]}> + { + setActiveStarterPack(undefined) + }} + accessibilityLabel={_(msg`Back`)} + accessibilityHint={_(msg`Go back to previous screen`)}> + + diff --git a/src/screens/StarterPack/StarterPackScreen.tsx b/src/screens/StarterPack/StarterPackScreen.tsx index aa0e75a2..679b3f2c 100644 --- a/src/screens/StarterPack/StarterPackScreen.tsx +++ b/src/screens/StarterPack/StarterPackScreen.tsx @@ -28,15 +28,20 @@ import {HITSLOP_20} from 'lib/constants' import {makeProfileLink, makeStarterPackLink} from 'lib/routes/links' import {CommonNavigatorParams, NavigationProp} from 'lib/routes/types' import {logEvent} from 'lib/statsig/statsig' -import {getStarterPackOgCard} from 'lib/strings/starter-pack' +import { + createStarterPackUri, + getStarterPackOgCard, +} from 'lib/strings/starter-pack' import {isWeb} from 'platform/detection' import {updateProfileShadow} from 'state/cache/profile-shadow' import {useModerationOpts} from 'state/preferences/moderation-opts' import {useListMembersQuery} from 'state/queries/list-members' +import {useResolvedStarterPackShortLink} from 'state/queries/resolve-short-link' 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 {useSetActiveStarterPack} from 'state/shell/starter-pack' import * as Toast from '#/view/com/util/Toast' import {PagerWithHeader} from 'view/com/pager/PagerWithHeader' import {ProfileSubpageHeader} from 'view/com/profile/ProfileSubpageHeader' @@ -67,12 +72,77 @@ type StarterPackScreeProps = NativeStackScreenProps< CommonNavigatorParams, 'StarterPack' > +type StarterPackScreenShortProps = NativeStackScreenProps< + CommonNavigatorParams, + 'StarterPackShort' +> export function StarterPackScreen({route}: StarterPackScreeProps) { + return +} + +export function StarterPackScreenShort({route}: StarterPackScreenShortProps) { + const {_} = useLingui() + const { + data: resolvedStarterPack, + isLoading, + isError, + } = useResolvedStarterPackShortLink({ + code: route.params.code, + }) + + if (isLoading || isError || !resolvedStarterPack) { + return ( + + ) + } + return +} + +export function StarterPackAuthCheck({ + routeParams, +}: { + routeParams: StarterPackScreeProps['route']['params'] +}) { + const navigation = useNavigation() + const setActiveStarterPack = useSetActiveStarterPack() + const {currentAccount} = useSession() + + React.useEffect(() => { + if (currentAccount) return + + const uri = createStarterPackUri({ + did: routeParams.name, + rkey: routeParams.rkey, + }) + + if (!uri) return + setActiveStarterPack({ + uri, + }) + + navigation.goBack() + }, [routeParams, currentAccount, navigation, setActiveStarterPack]) + + if (!currentAccount) return null + + return +} + +export function StarterPackScreenInner({ + routeParams, +}: { + routeParams: StarterPackScreeProps['route']['params'] +}) { + const {name, rkey} = routeParams const {_} = useLingui() const {currentAccount} = useSession() - const {name, rkey} = route.params const moderationOpts = useModerationOpts() const { data: did, @@ -113,16 +183,16 @@ export function StarterPackScreen({route}: StarterPackScreeProps) { } return ( - ) } -function StarterPackScreenInner({ +function StarterPackScreenLoaded({ starterPack, routeParams, listMembersQuery, diff --git a/src/state/queries/resolve-short-link.ts b/src/state/queries/resolve-short-link.ts new file mode 100644 index 00000000..a10bc12c --- /dev/null +++ b/src/state/queries/resolve-short-link.ts @@ -0,0 +1,24 @@ +import {useQuery} from '@tanstack/react-query' + +import {resolveShortLink} from 'lib/link-meta/resolve-short-link' +import {parseStarterPackUri} from 'lib/strings/starter-pack' +import {STALE} from 'state/queries/index' + +const ROOT_URI = 'https://go.bsky.app/' + +const RQKEY_ROOT = 'resolved-short-link' +export const RQKEY = (code: string) => [RQKEY_ROOT, code] + +export function useResolvedStarterPackShortLink({code}: {code: string}) { + return useQuery({ + queryKey: RQKEY(code), + queryFn: async () => { + const uri = `${ROOT_URI}${code}` + const res = await resolveShortLink(uri) + return parseStarterPackUri(res) + }, + retry: 1, + enabled: Boolean(code), + staleTime: STALE.HOURS.ONE, + }) +} diff --git a/src/state/shell/logged-out.tsx b/src/state/shell/logged-out.tsx index dc78d03d..2c577fdd 100644 --- a/src/state/shell/logged-out.tsx +++ b/src/state/shell/logged-out.tsx @@ -50,6 +50,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) { const activeStarterPack = useActiveStarterPack() const {hasSession} = useSession() const shouldShowStarterPack = Boolean(activeStarterPack?.uri) && !hasSession + const [state, setState] = React.useState({ showLoggedOut: shouldShowStarterPack, requestedAccountSwitchTo: shouldShowStarterPack @@ -59,6 +60,25 @@ export function Provider({children}: React.PropsWithChildren<{}>) { : undefined, }) + const [prevActiveStarterPack, setPrevActiveStarterPack] = + React.useState(activeStarterPack) + if (activeStarterPack?.uri !== prevActiveStarterPack?.uri) { + setPrevActiveStarterPack(activeStarterPack) + if (activeStarterPack) { + setState(s => ({ + ...s, + showLoggedOut: true, + requestedAccountSwitchTo: 'starterpack', + })) + } else { + setState(s => ({ + ...s, + showLoggedOut: false, + requestedAccountSwitchTo: undefined, + })) + } + } + const controls = React.useMemo( () => ({ setShowLoggedOut(show) {