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

View File

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

View File

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

View File

@ -28,10 +28,7 @@ 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 {
createStarterPackUri,
getStarterPackOgCard,
} from 'lib/strings/starter-pack'
import {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'
@ -41,6 +38,7 @@ 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 {useLoggedOutViewControls} from 'state/shell/logged-out'
import {useSetActiveStarterPack} from 'state/shell/starter-pack'
import * as Toast from '#/view/com/util/Toast'
import {PagerWithHeader} from 'view/com/pager/PagerWithHeader'
@ -78,7 +76,7 @@ type StarterPackScreenShortProps = NativeStackScreenProps<
>
export function StarterPackScreen({route}: StarterPackScreeProps) {
return <StarterPackAuthCheck routeParams={route.params} />
return <StarterPackScreenInner routeParams={route.params} />
}
export function StarterPackScreenShort({route}: StarterPackScreenShortProps) {
@ -101,37 +99,7 @@ export function StarterPackScreenShort({route}: StarterPackScreenShortProps) {
/>
)
}
return <StarterPackAuthCheck 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} />
return <StarterPackScreenInner routeParams={resolvedStarterPack} />
}
export function StarterPackScreenInner({
@ -330,9 +298,11 @@ function Header({
}) {
const {_} = useLingui()
const t = useTheme()
const {currentAccount} = useSession()
const {currentAccount, hasSession} = useSession()
const agent = useAgent()
const queryClient = useQueryClient()
const setActiveStarterPack = useSetActiveStarterPack()
const {requestSwitchToAccount} = useLoggedOutViewControls()
const [isProcessing, setIsProcessing] = React.useState(false)
@ -340,6 +310,29 @@ function Header({
const isOwn = creator?.did === currentAccount?.did
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 () => {
if (!starterPack.list) return
@ -397,45 +390,64 @@ function Header({
avatar={undefined}
creator={creator}
avatarType="starter-pack">
<View style={[a.flex_row, a.gap_sm, a.align_center]}>
{isOwn ? (
<Button
label={_(msg`Share this starter pack`)}
hitSlop={HITSLOP_20}
variant="solid"
color="primary"
size="small"
onPress={onOpenShareDialog}>
<ButtonText>
<Trans>Share</Trans>
</ButtonText>
</Button>
) : (
<Button
label={_(msg`Follow all`)}
variant="solid"
color="primary"
size="small"
disabled={isProcessing}
onPress={onFollowAll}>
<ButtonText>
<Trans>Follow all</Trans>
{isProcessing && <Loader size="xs" />}
</ButtonText>
</Button>
)}
<OverflowMenu
routeParams={routeParams}
starterPack={starterPack}
onOpenShareDialog={onOpenShareDialog}
/>
</View>
{hasSession ? (
<View style={[a.flex_row, a.gap_sm, a.align_center]}>
{isOwn ? (
<Button
label={_(msg`Share this starter pack`)}
hitSlop={HITSLOP_20}
variant="solid"
color="primary"
size="small"
onPress={onOpenShareDialog}>
<ButtonText>
<Trans>Share</Trans>
</ButtonText>
</Button>
) : (
<Button
label={_(msg`Follow all`)}
variant="solid"
color="primary"
size="small"
disabled={isProcessing}
onPress={onFollowAll}>
<ButtonText>
<Trans>Follow all</Trans>
{isProcessing && <Loader size="xs" />}
</ButtonText>
</Button>
)}
<OverflowMenu
routeParams={routeParams}
starterPack={starterPack}
onOpenShareDialog={onOpenShareDialog}
/>
</View>
) : null}
</ProfileSubpageHeader>
{richText || joinedAllTimeCount >= 25 ? (
{!hasSession || richText || joinedAllTimeCount >= 25 ? (
<View style={[a.px_lg, a.pt_md, a.pb_sm, a.gap_md]}>
{richText ? (
<RichText value={richText} style={[a.text_md, a.leading_snug]} />
) : 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 ? (
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
<FontAwesomeIcon

View File

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

View File

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