Handle pushing to starterpack screen when unauthed (#4692)

zio/stable
Hailey 2024-06-27 21:44:26 -07:00 committed by GitHub
parent 91c4aa7c2d
commit 8ebf9cc4b1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 143 additions and 132 deletions

View File

@ -1,5 +1,5 @@
import React from 'react' import React, {useEffect} from 'react'
import {View} from 'react-native' import {LayoutAnimation, View} from 'react-native'
import {msg, Trans} from '@lingui/macro' import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
@ -27,10 +27,18 @@ function sanitizeDate(date: Date): Date {
return date return date
} }
export function StepInfo() { export function StepInfo({
isLoadingStarterPack,
}: {
isLoadingStarterPack: boolean
}) {
const {_} = useLingui() const {_} = useLingui()
const {state, dispatch} = useSignupContext() const {state, dispatch} = useSignupContext()
useEffect(() => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
}, [state.isLoading, isLoadingStarterPack])
return ( return (
<ScreenTransition> <ScreenTransition>
<View style={[a.gap_md]}> <View style={[a.gap_md]}>
@ -46,7 +54,7 @@ export function StepInfo() {
} }
/> />
</View> </View>
{state.isLoading ? ( {state.isLoading || isLoadingStarterPack ? (
<View style={[a.align_center]}> <View style={[a.align_center]}>
<Loader size="xl" /> <Loader size="xl" />
</View> </View>

View File

@ -1,10 +1,6 @@
import React from 'react' import React from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import Animated, { import {LayoutAnimationConfig} from 'react-native-reanimated'
FadeIn,
FadeOut,
LayoutAnimationConfig,
} from 'react-native-reanimated'
import {AppBskyGraphStarterpack} from '@atproto/api' import {AppBskyGraphStarterpack} from '@atproto/api'
import {msg, Trans} from '@lingui/macro' import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
@ -47,9 +43,15 @@ export function Signup({onPressBack}: {onPressBack: () => void}) {
const agent = useAgent() const agent = useAgent()
const activeStarterPack = useActiveStarterPack() const activeStarterPack = useActiveStarterPack()
const {data: starterPack} = useStarterPackQuery({ const {
data: starterPack,
isFetching: isFetchingStarterPack,
isError: isErrorStarterPack,
} = useStarterPackQuery({
uri: activeStarterPack?.uri, uri: activeStarterPack?.uri,
}) })
const showStarterPackCard =
activeStarterPack?.uri && !isFetchingStarterPack && starterPack
const { const {
data: serviceInfo, data: serviceInfo,
@ -155,30 +157,27 @@ export function Signup({onPressBack}: {onPressBack: () => void}) {
description={_(msg`We're so excited to have you join us!`)} description={_(msg`We're so excited to have you join us!`)}
scrollable> scrollable>
<View testID="createAccount" style={a.flex_1}> <View testID="createAccount" style={a.flex_1}>
{state.activeStep === SignupStep.INFO && {showStarterPackCard &&
starterPack &&
AppBskyGraphStarterpack.isRecord(starterPack.record) ? ( AppBskyGraphStarterpack.isRecord(starterPack.record) ? (
<Animated.View entering={FadeIn} exiting={FadeOut}> <LinearGradientBackground
<LinearGradientBackground style={[a.mx_lg, a.p_lg, a.gap_sm, a.rounded_sm]}>
style={[a.mx_lg, a.p_lg, a.gap_sm, a.rounded_sm]}> <Text style={[a.font_bold, a.text_xl, {color: 'white'}]}>
<Text style={[a.font_bold, a.text_xl, {color: 'white'}]}> {starterPack.record.name}
{starterPack.record.name} </Text>
</Text> <Text style={[{color: 'white'}]}>
<Text style={[{color: 'white'}]}> {starterPack.feeds?.length ? (
{starterPack.feeds?.length ? ( <Trans>
<Trans> You'll follow the suggested users and feeds once you finish
You'll follow the suggested users and feeds once you creating your account!
finish creating your account! </Trans>
</Trans> ) : (
) : ( <Trans>
<Trans> You'll follow the suggested users once you finish creating
You'll follow the suggested users once you finish creating your account!
your account! </Trans>
</Trans> )}
)} </Text>
</Text> </LinearGradientBackground>
</LinearGradientBackground>
</Animated.View>
) : null} ) : null}
<View <View
style={[ style={[
@ -211,7 +210,11 @@ export function Signup({onPressBack}: {onPressBack: () => void}) {
<View style={[a.pb_3xl]}> <View style={[a.pb_3xl]}>
<LayoutAnimationConfig skipEntering skipExiting> <LayoutAnimationConfig skipEntering skipExiting>
{state.activeStep === SignupStep.INFO ? ( {state.activeStep === SignupStep.INFO ? (
<StepInfo /> <StepInfo
isLoadingStarterPack={
isFetchingStarterPack && !isErrorStarterPack
}
/>
) : state.activeStep === SignupStep.HANDLE ? ( ) : state.activeStep === SignupStep.HANDLE ? (
<StepHandle /> <StepHandle />
) : ( ) : (

View File

@ -116,8 +116,6 @@ export function reducer(s: SignupState, a: SignupAction): SignupState {
break break
} }
case 'setServiceDescription': { case 'setServiceDescription': {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
next.serviceDescription = a.value next.serviceDescription = a.value
next.userDomain = a.value?.availableUserDomains[0] ?? '' next.userDomain = a.value?.availableUserDomains[0] ?? ''
next.isLoading = false next.isLoading = false

View File

@ -28,10 +28,7 @@ import {HITSLOP_20} from 'lib/constants'
import {makeProfileLink, makeStarterPackLink} from 'lib/routes/links' import {makeProfileLink, makeStarterPackLink} from 'lib/routes/links'
import {CommonNavigatorParams, NavigationProp} from 'lib/routes/types' import {CommonNavigatorParams, NavigationProp} from 'lib/routes/types'
import {logEvent} from 'lib/statsig/statsig' import {logEvent} from 'lib/statsig/statsig'
import { import {getStarterPackOgCard} from 'lib/strings/starter-pack'
createStarterPackUri,
getStarterPackOgCard,
} from 'lib/strings/starter-pack'
import {isWeb} from 'platform/detection' import {isWeb} from 'platform/detection'
import {updateProfileShadow} from 'state/cache/profile-shadow' import {updateProfileShadow} from 'state/cache/profile-shadow'
import {useModerationOpts} from 'state/preferences/moderation-opts' import {useModerationOpts} from 'state/preferences/moderation-opts'
@ -41,6 +38,7 @@ import {useResolveDidQuery} from 'state/queries/resolve-uri'
import {useShortenLink} from 'state/queries/shorten-link' import {useShortenLink} from 'state/queries/shorten-link'
import {useStarterPackQuery} from 'state/queries/starter-packs' import {useStarterPackQuery} from 'state/queries/starter-packs'
import {useAgent, useSession} from 'state/session' import {useAgent, useSession} from 'state/session'
import {useLoggedOutViewControls} from 'state/shell/logged-out'
import {useSetActiveStarterPack} from 'state/shell/starter-pack' import {useSetActiveStarterPack} from 'state/shell/starter-pack'
import * as Toast from '#/view/com/util/Toast' import * as Toast from '#/view/com/util/Toast'
import {PagerWithHeader} from 'view/com/pager/PagerWithHeader' import {PagerWithHeader} from 'view/com/pager/PagerWithHeader'
@ -78,7 +76,7 @@ type StarterPackScreenShortProps = NativeStackScreenProps<
> >
export function StarterPackScreen({route}: StarterPackScreeProps) { export function StarterPackScreen({route}: StarterPackScreeProps) {
return <StarterPackAuthCheck routeParams={route.params} /> return <StarterPackScreenInner routeParams={route.params} />
} }
export function StarterPackScreenShort({route}: StarterPackScreenShortProps) { export function StarterPackScreenShort({route}: StarterPackScreenShortProps) {
@ -101,37 +99,7 @@ export function StarterPackScreenShort({route}: StarterPackScreenShortProps) {
/> />
) )
} }
return <StarterPackAuthCheck routeParams={resolvedStarterPack} /> return <StarterPackScreenInner routeParams={resolvedStarterPack} />
}
export function StarterPackAuthCheck({
routeParams,
}: {
routeParams: StarterPackScreeProps['route']['params']
}) {
const navigation = useNavigation<NavigationProp>()
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 <StarterPackScreenInner routeParams={routeParams} />
} }
export function StarterPackScreenInner({ export function StarterPackScreenInner({
@ -330,9 +298,11 @@ function Header({
}) { }) {
const {_} = useLingui() const {_} = useLingui()
const t = useTheme() const t = useTheme()
const {currentAccount} = useSession() const {currentAccount, hasSession} = useSession()
const agent = useAgent() const agent = useAgent()
const queryClient = useQueryClient() const queryClient = useQueryClient()
const setActiveStarterPack = useSetActiveStarterPack()
const {requestSwitchToAccount} = useLoggedOutViewControls()
const [isProcessing, setIsProcessing] = React.useState(false) const [isProcessing, setIsProcessing] = React.useState(false)
@ -340,6 +310,29 @@ function Header({
const isOwn = creator?.did === currentAccount?.did const isOwn = creator?.did === currentAccount?.did
const joinedAllTimeCount = starterPack.joinedAllTimeCount ?? 0 const joinedAllTimeCount = starterPack.joinedAllTimeCount ?? 0
const navigation = useNavigation<NavigationProp>()
React.useEffect(() => {
const onFocus = () => {
if (hasSession) return
setActiveStarterPack({
uri: starterPack.uri,
})
}
const onBeforeRemove = () => {
if (hasSession) return
setActiveStarterPack(undefined)
}
navigation.addListener('focus', onFocus)
navigation.addListener('beforeRemove', onBeforeRemove)
return () => {
navigation.removeListener('focus', onFocus)
navigation.removeListener('beforeRemove', onBeforeRemove)
}
}, [hasSession, navigation, setActiveStarterPack, starterPack.uri])
const onFollowAll = async () => { const onFollowAll = async () => {
if (!starterPack.list) return if (!starterPack.list) return
@ -397,45 +390,64 @@ function Header({
avatar={undefined} avatar={undefined}
creator={creator} creator={creator}
avatarType="starter-pack"> avatarType="starter-pack">
<View style={[a.flex_row, a.gap_sm, a.align_center]}> {hasSession ? (
{isOwn ? ( <View style={[a.flex_row, a.gap_sm, a.align_center]}>
<Button {isOwn ? (
label={_(msg`Share this starter pack`)} <Button
hitSlop={HITSLOP_20} label={_(msg`Share this starter pack`)}
variant="solid" hitSlop={HITSLOP_20}
color="primary" variant="solid"
size="small" color="primary"
onPress={onOpenShareDialog}> size="small"
<ButtonText> onPress={onOpenShareDialog}>
<Trans>Share</Trans> <ButtonText>
</ButtonText> <Trans>Share</Trans>
</Button> </ButtonText>
) : ( </Button>
<Button ) : (
label={_(msg`Follow all`)} <Button
variant="solid" label={_(msg`Follow all`)}
color="primary" variant="solid"
size="small" color="primary"
disabled={isProcessing} size="small"
onPress={onFollowAll}> disabled={isProcessing}
<ButtonText> onPress={onFollowAll}>
<Trans>Follow all</Trans> <ButtonText>
{isProcessing && <Loader size="xs" />} <Trans>Follow all</Trans>
</ButtonText> {isProcessing && <Loader size="xs" />}
</Button> </ButtonText>
)} </Button>
<OverflowMenu )}
routeParams={routeParams} <OverflowMenu
starterPack={starterPack} routeParams={routeParams}
onOpenShareDialog={onOpenShareDialog} starterPack={starterPack}
/> onOpenShareDialog={onOpenShareDialog}
</View> />
</View>
) : null}
</ProfileSubpageHeader> </ProfileSubpageHeader>
{richText || joinedAllTimeCount >= 25 ? ( {!hasSession || richText || joinedAllTimeCount >= 25 ? (
<View style={[a.px_lg, a.pt_md, a.pb_sm, a.gap_md]}> <View style={[a.px_lg, a.pt_md, a.pb_sm, a.gap_md]}>
{richText ? ( {richText ? (
<RichText value={richText} style={[a.text_md, a.leading_snug]} /> <RichText value={richText} style={[a.text_md, a.leading_snug]} />
) : null} ) : null}
{!hasSession ? (
<Button
label={_(msg`Join Bluesky`)}
onPress={() => {
setActiveStarterPack({
uri: starterPack.uri,
})
requestSwitchToAccount({requestedAccount: 'new'})
}}
variant="solid"
color="primary"
size="medium">
<ButtonText style={[a.text_lg]}>
<Trans>Join Bluesky</Trans>
</ButtonText>
</Button>
) : null}
{joinedAllTimeCount >= 25 ? ( {joinedAllTimeCount >= 25 ? (
<View style={[a.flex_row, a.align_center, a.gap_sm]}> <View style={[a.flex_row, a.align_center, a.gap_sm]}>
<FontAwesomeIcon <FontAwesomeIcon

View File

@ -25,13 +25,22 @@ import {
parseStarterPackUri, parseStarterPackUri,
} from 'lib/strings/starter-pack' } from 'lib/strings/starter-pack'
import {invalidateActorStarterPacksQuery} from 'state/queries/actor-starter-packs' import {invalidateActorStarterPacksQuery} from 'state/queries/actor-starter-packs'
import {STALE} from 'state/queries/index'
import {invalidateListMembersQuery} from 'state/queries/list-members' import {invalidateListMembersQuery} from 'state/queries/list-members'
import {useAgent} from 'state/session' import {useAgent} from 'state/session'
const RQKEY_ROOT = 'starter-pack' const RQKEY_ROOT = 'starter-pack'
const RQKEY = (did?: string, rkey?: string) => { const RQKEY = ({
if (did?.startsWith('https://') || did?.startsWith('at://')) { uri,
const parsed = parseStarterPackUri(did) did,
rkey,
}: {
uri?: string
did?: string
rkey?: string
}) => {
if (uri?.startsWith('https://') || uri?.startsWith('at://')) {
const parsed = parseStarterPackUri(uri)
return [RQKEY_ROOT, parsed?.name, parsed?.rkey] return [RQKEY_ROOT, parsed?.name, parsed?.rkey]
} else { } else {
return [RQKEY_ROOT, did, rkey] return [RQKEY_ROOT, did, rkey]
@ -50,7 +59,7 @@ export function useStarterPackQuery({
const agent = useAgent() const agent = useAgent()
return useQuery<StarterPackView>({ return useQuery<StarterPackView>({
queryKey: RQKEY(did, rkey), queryKey: RQKEY(uri ? {uri} : {did, rkey}),
queryFn: async () => { queryFn: async () => {
if (!uri) { if (!uri) {
uri = `at://${did}/app.bsky.graph.starterpack/${rkey}` uri = `at://${did}/app.bsky.graph.starterpack/${rkey}`
@ -64,6 +73,7 @@ export function useStarterPackQuery({
return res.data.starterPack return res.data.starterPack
}, },
enabled: Boolean(uri) || Boolean(did && rkey), enabled: Boolean(uri) || Boolean(did && rkey),
staleTime: STALE.MINUTES.FIVE,
}) })
} }
@ -76,7 +86,7 @@ export async function invalidateStarterPack({
did: string did: string
rkey: string rkey: string
}) { }) {
await queryClient.invalidateQueries({queryKey: RQKEY(did, rkey)}) await queryClient.invalidateQueries({queryKey: RQKEY({did, rkey})})
} }
interface UseCreateStarterPackMutationParams { interface UseCreateStarterPackMutationParams {

View File

@ -50,7 +50,6 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
const activeStarterPack = useActiveStarterPack() const activeStarterPack = useActiveStarterPack()
const {hasSession} = useSession() const {hasSession} = useSession()
const shouldShowStarterPack = Boolean(activeStarterPack?.uri) && !hasSession const shouldShowStarterPack = Boolean(activeStarterPack?.uri) && !hasSession
const [state, setState] = React.useState<State>({ const [state, setState] = React.useState<State>({
showLoggedOut: shouldShowStarterPack, showLoggedOut: shouldShowStarterPack,
requestedAccountSwitchTo: shouldShowStarterPack requestedAccountSwitchTo: shouldShowStarterPack
@ -60,25 +59,6 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
: undefined, : 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<Controls>( const controls = React.useMemo<Controls>(
() => ({ () => ({
setShowLoggedOut(show) { setShowLoggedOut(show) {