about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/components/StarterPack/ProfileStarterPacks.tsx57
-rw-r--r--src/state/queries/actor-starter-packs.ts10
-rw-r--r--src/view/com/feeds/ProfileFeedgens.tsx2
-rw-r--r--src/view/com/lists/ProfileLists.tsx2
-rw-r--r--src/view/screens/Profile.tsx21
5 files changed, 42 insertions, 50 deletions
diff --git a/src/components/StarterPack/ProfileStarterPacks.tsx b/src/components/StarterPack/ProfileStarterPacks.tsx
index d8925684b..da5f0dc6a 100644
--- a/src/components/StarterPack/ProfileStarterPacks.tsx
+++ b/src/components/StarterPack/ProfileStarterPacks.tsx
@@ -1,4 +1,9 @@
-import React from 'react'
+import React, {
+  useCallback,
+  useEffect,
+  useImperativeHandle,
+  useState,
+} from 'react'
 import {
   findNodeHandle,
   ListRenderItemInfo,
@@ -6,11 +11,10 @@ import {
   View,
   ViewStyle,
 } from 'react-native'
-import {AppBskyGraphDefs, AppBskyGraphGetActorStarterPacks} from '@atproto/api'
+import {AppBskyGraphDefs} from '@atproto/api'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 import {useNavigation} from '@react-navigation/native'
-import {InfiniteData, UseInfiniteQueryResult} from '@tanstack/react-query'
 
 import {useGenerateStarterPackMutation} from '#/lib/generate-starterpack'
 import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset'
@@ -19,28 +23,27 @@ import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
 import {NavigationProp} from '#/lib/routes/types'
 import {parseStarterPackUri} from '#/lib/strings/starter-pack'
 import {logger} from '#/logger'
+import {useActorStarterPacksQuery} from '#/state/queries/actor-starter-packs'
 import {List, ListRef} from '#/view/com/util/List'
-import {Text} from '#/view/com/util/text/Text'
+import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
 import {atoms as a, ios, useTheme} from '#/alf'
 import {Button, ButtonIcon, ButtonText} from '#/components/Button'
 import {useDialogControl} from '#/components/Dialog'
+import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog'
+import {PlusSmall_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
 import {LinearGradientBackground} from '#/components/LinearGradientBackground'
 import {Loader} from '#/components/Loader'
 import * as Prompt from '#/components/Prompt'
 import {Default as StarterPackCard} from '#/components/StarterPack/StarterPackCard'
-import {VerifyEmailDialog} from '../dialogs/VerifyEmailDialog'
-import {PlusSmall_Stroke2_Corner0_Rounded as Plus} from '../icons/Plus'
+import {Text} from '#/components/Typography'
 
 interface SectionRef {
   scrollToTop: () => void
 }
 
 interface ProfileFeedgensProps {
-  starterPacksQuery: UseInfiniteQueryResult<
-    InfiniteData<AppBskyGraphGetActorStarterPacks.OutputSchema, unknown>,
-    Error
-  >
   scrollElRef: ListRef
+  did: string
   headerOffset: number
   enabled?: boolean
   style?: StyleProp<ViewStyle>
@@ -58,8 +61,8 @@ export const ProfileStarterPacks = React.forwardRef<
   ProfileFeedgensProps
 >(function ProfileFeedgensImpl(
   {
-    starterPacksQuery: query,
     scrollElRef,
+    did,
     headerOffset,
     enabled,
     style,
@@ -71,17 +74,18 @@ export const ProfileStarterPacks = React.forwardRef<
 ) {
   const t = useTheme()
   const bottomBarOffset = useBottomBarOffset(100)
-  const [isPTRing, setIsPTRing] = React.useState(false)
-  const {data, refetch, isFetching, hasNextPage, fetchNextPage} = query
+  const [isPTRing, setIsPTRing] = useState(false)
+  const {data, refetch, isFetching, hasNextPage, fetchNextPage} =
+    useActorStarterPacksQuery({did, enabled})
   const {isTabletOrDesktop} = useWebMediaQueries()
 
   const items = data?.pages.flatMap(page => page.starterPacks)
 
-  React.useImperativeHandle(ref, () => ({
+  useImperativeHandle(ref, () => ({
     scrollToTop: () => {},
   }))
 
-  const onRefresh = React.useCallback(async () => {
+  const onRefresh = useCallback(async () => {
     setIsPTRing(true)
     try {
       await refetch()
@@ -91,7 +95,7 @@ export const ProfileStarterPacks = React.forwardRef<
     setIsPTRing(false)
   }, [refetch, setIsPTRing])
 
-  const onEndReached = React.useCallback(async () => {
+  const onEndReached = useCallback(async () => {
     if (isFetching || !hasNextPage) return
 
     try {
@@ -101,7 +105,7 @@ export const ProfileStarterPacks = React.forwardRef<
     }
   }, [isFetching, hasNextPage, fetchNextPage])
 
-  React.useEffect(() => {
+  useEffect(() => {
     if (enabled && scrollElRef.current) {
       const nativeTag = findNodeHandle(scrollElRef.current)
       setScrollViewTag(nativeTag)
@@ -140,9 +144,11 @@ export const ProfileStarterPacks = React.forwardRef<
         desktopFixedHeight
         onEndReached={onEndReached}
         onRefresh={onRefresh}
-        ListEmptyComponent={Empty}
+        ListEmptyComponent={
+          data ? (isMe ? Empty : undefined) : FeedLoadingPlaceholder
+        }
         ListFooterComponent={
-          items?.length !== 0 && isMe ? CreateAnother : undefined
+          !!data && items?.length !== 0 && isMe ? CreateAnother : undefined
         }
       />
     </View>
@@ -181,7 +187,6 @@ function CreateAnother() {
 
 function Empty() {
   const {_} = useLingui()
-  const t = useTheme()
   const navigation = useNavigation<NavigationProp>()
   const confirmDialogControl = useDialogControl()
   const followersDialogControl = useDialogControl()
@@ -190,7 +195,7 @@ function Empty() {
   const {needsEmailVerification} = useEmail()
   const verifyEmailControl = useDialogControl()
 
-  const [isGenerating, setIsGenerating] = React.useState(false)
+  const [isGenerating, setIsGenerating] = useState(false)
 
   const {mutate: generateStarterPack} = useGenerateStarterPackMutation({
     onSuccess: ({uri}) => {
@@ -227,16 +232,10 @@ function Empty() {
         a.justify_between,
         a.gap_lg,
         a.shadow_lg,
-        {marginTop: 1},
+        {marginTop: a.border.borderWidth},
       ]}>
       <View style={[a.gap_xs]}>
-        <Text
-          style={[
-            a.font_bold,
-            a.text_lg,
-            t.atoms.text_contrast_medium,
-            {color: 'white'},
-          ]}>
+        <Text style={[a.font_bold, a.text_lg, {color: 'white'}]}>
           <Trans>You haven't created a starter pack yet!</Trans>
         </Text>
         <Text style={[a.text_md, {color: 'white'}]}>
diff --git a/src/state/queries/actor-starter-packs.ts b/src/state/queries/actor-starter-packs.ts
index 487bcdfd9..670544dfe 100644
--- a/src/state/queries/actor-starter-packs.ts
+++ b/src/state/queries/actor-starter-packs.ts
@@ -11,7 +11,13 @@ import {useAgent} from '#/state/session'
 export const RQKEY_ROOT = 'actor-starter-packs'
 export const RQKEY = (did?: string) => [RQKEY_ROOT, did]
 
-export function useActorStarterPacksQuery({did}: {did?: string}) {
+export function useActorStarterPacksQuery({
+  did,
+  enabled = true,
+}: {
+  did?: string
+  enabled?: boolean
+}) {
   const agent = useAgent()
 
   return useInfiniteQuery<
@@ -30,7 +36,7 @@ export function useActorStarterPacksQuery({did}: {did?: string}) {
       })
       return res.data
     },
-    enabled: Boolean(did),
+    enabled: Boolean(did) && enabled,
     initialPageParam: undefined,
     getNextPageParam: lastPage => lastPage.cursor,
   })
diff --git a/src/view/com/feeds/ProfileFeedgens.tsx b/src/view/com/feeds/ProfileFeedgens.tsx
index f5894f9ee..b55c6b9bd 100644
--- a/src/view/com/feeds/ProfileFeedgens.tsx
+++ b/src/view/com/feeds/ProfileFeedgens.tsx
@@ -76,7 +76,7 @@ export const ProfileFeedgens = React.forwardRef<
     if (isError && isEmpty) {
       items = items.concat([ERROR_ITEM])
     }
-    if (!isFetched && isFetching) {
+    if (!isFetched || isFetching) {
       items = items.concat([LOADING])
     } else if (isEmpty) {
       items = items.concat([EMPTY])
diff --git a/src/view/com/lists/ProfileLists.tsx b/src/view/com/lists/ProfileLists.tsx
index d91a4fb66..3a0b0b198 100644
--- a/src/view/com/lists/ProfileLists.tsx
+++ b/src/view/com/lists/ProfileLists.tsx
@@ -72,7 +72,7 @@ export const ProfileLists = React.forwardRef<SectionRef, ProfileListsProps>(
       if (isError && isEmpty) {
         items = items.concat([ERROR_ITEM])
       }
-      if (!isFetched && isFetching) {
+      if (!isFetched || isFetching) {
         items = items.concat([LOADING])
       } else if (isEmpty) {
         items = items.concat([EMPTY])
diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx
index 4e0ac259f..f781ba2a8 100644
--- a/src/view/screens/Profile.tsx
+++ b/src/view/screens/Profile.tsx
@@ -3,7 +3,6 @@ import {StyleSheet} from 'react-native'
 import {SafeAreaView} from 'react-native-safe-area-context'
 import {
   AppBskyActorDefs,
-  AppBskyGraphGetActorStarterPacks,
   moderateProfile,
   ModerationOpts,
   RichText as RichTextAPI,
@@ -11,11 +10,7 @@ import {
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 import {useFocusEffect} from '@react-navigation/native'
-import {
-  InfiniteData,
-  UseInfiniteQueryResult,
-  useQueryClient,
-} from '@tanstack/react-query'
+import {useQueryClient} from '@tanstack/react-query'
 
 import {useSetTitle} from '#/lib/hooks/useSetTitle'
 import {ComposeIcon2} from '#/lib/icons'
@@ -27,7 +22,6 @@ import {colors, s} from '#/lib/styles'
 import {useProfileShadow} from '#/state/cache/profile-shadow'
 import {listenSoftReset} from '#/state/events'
 import {useModerationOpts} from '#/state/preferences/moderation-opts'
-import {useActorStarterPacksQuery} from '#/state/queries/actor-starter-packs'
 import {useLabelerInfoQuery} from '#/state/queries/labeler'
 import {resetProfilePostsQueries} from '#/state/queries/post-feed'
 import {useProfileQuery} from '#/state/queries/profile'
@@ -86,7 +80,6 @@ function ProfileScreenInner({route}: Props) {
   } = useProfileQuery({
     did: resolvedDid,
   })
-  const starterPacksQuery = useActorStarterPacksQuery({did: resolvedDid})
 
   const onPressTryAgain = React.useCallback(() => {
     if (resolveError) {
@@ -114,7 +107,7 @@ function ProfileScreenInner({route}: Props) {
   }, [queryClient, profile?.viewer?.blockedBy, resolvedDid])
 
   // Most pushes will happen here, since we will have only placeholder data
-  if (isLoadingDid || isLoadingProfile || starterPacksQuery.isLoading) {
+  if (isLoadingDid || isLoadingProfile) {
     return (
       <Layout.Content>
         <ProfileHeaderLoading />
@@ -138,7 +131,6 @@ function ProfileScreenInner({route}: Props) {
     return (
       <ProfileScreenLoaded
         profile={profile}
-        starterPacksQuery={starterPacksQuery}
         moderationOpts={moderationOpts}
         isPlaceholderProfile={isPlaceholderProfile}
         hideBackButton={!!route.params.hideBackButton}
@@ -164,16 +156,11 @@ function ProfileScreenLoaded({
   isPlaceholderProfile,
   moderationOpts,
   hideBackButton,
-  starterPacksQuery,
 }: {
   profile: AppBskyActorDefs.ProfileViewDetailed
   moderationOpts: ModerationOpts
   hideBackButton: boolean
   isPlaceholderProfile: boolean
-  starterPacksQuery: UseInfiniteQueryResult<
-    InfiniteData<AppBskyGraphGetActorStarterPacks.OutputSchema, unknown>,
-    Error
-  >
 }) {
   const profile = useProfileShadow(profileUnshadowed)
   const {hasSession, currentAccount} = useSession()
@@ -223,7 +210,7 @@ function ProfileScreenLoaded({
   const showLikesTab = isMe
   const showFeedsTab = isMe || (profile.associated?.feedgens || 0) > 0
   const showStarterPacksTab =
-    isMe || !!starterPacksQuery.data?.pages?.[0].starterPacks.length
+    isMe || (profile.associated?.starterPacks || 0) > 0
   const showListsTab =
     hasSession && (isMe || (profile.associated?.lists || 0) > 0)
 
@@ -487,8 +474,8 @@ function ProfileScreenLoaded({
           ? ({headerHeight, isFocused, scrollElRef}) => (
               <ProfileStarterPacks
                 ref={starterPacksSectionRef}
+                did={profile.did}
                 isMe={isMe}
-                starterPacksQuery={starterPacksQuery}
                 scrollElRef={scrollElRef as ListRef}
                 headerOffset={headerHeight}
                 enabled={isFocused}