about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoFallback.tsx12
-rw-r--r--src/components/Post/Embed/VideoEmbed/index.web.tsx8
-rw-r--r--src/components/StarterPack/ProfileStarterPacks.tsx44
-rw-r--r--src/components/verification/VerificationCheckButton.tsx6
-rw-r--r--src/screens/Profile/Sections/Feed.tsx38
-rw-r--r--src/screens/Profile/Sections/Labels.tsx1
-rw-r--r--src/view/com/feeds/ProfileFeedgens.tsx51
-rw-r--r--src/view/com/lists/ProfileLists.tsx349
8 files changed, 267 insertions, 242 deletions
diff --git a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoFallback.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoFallback.tsx
index 095136944..2994d6c48 100644
--- a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoFallback.tsx
+++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoFallback.tsx
@@ -3,7 +3,9 @@ import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 
 import {atoms as a, useTheme} from '#/alf'
-import {Button, ButtonText} from '#/components/Button'
+import {Button, ButtonIcon, ButtonText} from '#/components/Button'
+import {ArrowRotateCounterClockwise_Stroke2_Corner0_Rounded as ArrowRotateIcon} from '#/components/icons/ArrowRotateCounterClockwise'
+import {MediaInsetBorder} from '#/components/MediaInsetBorder'
 import {Text as TypoText} from '#/components/Typography'
 
 export function Container({children}: {children: React.ReactNode}) {
@@ -16,12 +18,12 @@ export function Container({children}: {children: React.ReactNode}) {
         a.justify_center,
         a.align_center,
         a.px_lg,
-        a.border,
-        t.atoms.border_contrast_low,
-        a.rounded_sm,
+        a.rounded_md,
+        a.overflow_hidden,
         a.gap_lg,
       ]}>
       {children}
+      <MediaInsetBorder />
     </View>
   )
 }
@@ -50,8 +52,8 @@ export function RetryButton({onPress}: {onPress: () => void}) {
       onPress={onPress}
       size="small"
       color="secondary_inverted"
-      variant="solid"
       label={_(msg`Retry`)}>
+      <ButtonIcon icon={ArrowRotateIcon} />
       <ButtonText>
         <Trans>Retry</Trans>
       </ButtonText>
diff --git a/src/components/Post/Embed/VideoEmbed/index.web.tsx b/src/components/Post/Embed/VideoEmbed/index.web.tsx
index 25f9f4604..e988bc830 100644
--- a/src/components/Post/Embed/VideoEmbed/index.web.tsx
+++ b/src/components/Post/Embed/VideoEmbed/index.web.tsx
@@ -86,7 +86,13 @@ export function VideoEmbed({
   const contents = (
     <div
       ref={ref}
-      style={{display: 'flex', flex: 1, cursor: 'default'}}
+      style={{
+        display: 'flex',
+        flex: 1,
+        cursor: 'default',
+        backgroundImage: `url(${embed.thumbnail})`,
+        backgroundSize: 'cover',
+      }}
       onClick={evt => evt.stopPropagation()}>
       <ErrorBoundary renderError={renderError} key={key}>
         <OnlyNearScreen>
diff --git a/src/components/StarterPack/ProfileStarterPacks.tsx b/src/components/StarterPack/ProfileStarterPacks.tsx
index bbe0bc52b..7252a1162 100644
--- a/src/components/StarterPack/ProfileStarterPacks.tsx
+++ b/src/components/StarterPack/ProfileStarterPacks.tsx
@@ -1,13 +1,9 @@
-import React, {
-  useCallback,
-  useEffect,
-  useImperativeHandle,
-  useState,
-} from 'react'
+import {useCallback, useEffect, useImperativeHandle, useState} from 'react'
 import {
   findNodeHandle,
   type ListRenderItemInfo,
   type StyleProp,
+  useWindowDimensions,
   View,
   type ViewStyle,
 } from 'react-native'
@@ -42,6 +38,7 @@ interface SectionRef {
 }
 
 interface ProfileFeedgensProps {
+  ref?: React.Ref<SectionRef>
   scrollElRef: ListRef
   did: string
   headerOffset: number
@@ -56,24 +53,20 @@ function keyExtractor(item: AppBskyGraphDefs.StarterPackView) {
   return item.uri
 }
 
-export const ProfileStarterPacks = React.forwardRef<
-  SectionRef,
-  ProfileFeedgensProps
->(function ProfileFeedgensImpl(
-  {
-    scrollElRef,
-    did,
-    headerOffset,
-    enabled,
-    style,
-    testID,
-    setScrollViewTag,
-    isMe,
-  },
+export function ProfileStarterPacks({
   ref,
-) {
+  scrollElRef,
+  did,
+  headerOffset,
+  enabled,
+  style,
+  testID,
+  setScrollViewTag,
+  isMe,
+}: ProfileFeedgensProps) {
   const t = useTheme()
   const bottomBarOffset = useBottomBarOffset(100)
+  const {height} = useWindowDimensions()
   const [isPTRing, setIsPTRing] = useState(false)
   const {
     data,
@@ -101,7 +94,7 @@ export const ProfileStarterPacks = React.forwardRef<
     setIsPTRing(false)
   }, [refetch, setIsPTRing])
 
-  const onEndReached = React.useCallback(async () => {
+  const onEndReached = useCallback(async () => {
     if (isFetchingNextPage || !hasNextPage || isError) return
     try {
       await fetchNextPage()
@@ -144,7 +137,10 @@ export const ProfileStarterPacks = React.forwardRef<
         refreshing={isPTRing}
         headerOffset={headerOffset}
         progressViewOffset={ios(0)}
-        contentContainerStyle={{paddingBottom: headerOffset + bottomBarOffset}}
+        contentContainerStyle={{
+          minHeight: height + headerOffset,
+          paddingBottom: bottomBarOffset,
+        }}
         removeClippedSubviews={true}
         desktopFixedHeight
         onEndReached={onEndReached}
@@ -158,7 +154,7 @@ export const ProfileStarterPacks = React.forwardRef<
       />
     </View>
   )
-})
+}
 
 function CreateAnother() {
   const {_} = useLingui()
diff --git a/src/components/verification/VerificationCheckButton.tsx b/src/components/verification/VerificationCheckButton.tsx
index 3f3bb2095..45449f43c 100644
--- a/src/components/verification/VerificationCheckButton.tsx
+++ b/src/components/verification/VerificationCheckButton.tsx
@@ -99,15 +99,15 @@ export function Badge({
             : _(msg`View this user's verifications`)
         }
         hitSlop={20}
-        onPress={() => {
+        onPress={evt => {
+          evt.preventDefault()
           logger.metric('verification:badge:click', {}, {statsig: true})
           if (state.profile.role === 'verifier') {
             verifierDialogControl.open()
           } else {
             verificationsDialogControl.open()
           }
-        }}
-        style={[]}>
+        }}>
         {({hovered}) => (
           <View
             style={[
diff --git a/src/screens/Profile/Sections/Feed.tsx b/src/screens/Profile/Sections/Feed.tsx
index e0c3e221f..2f54eda7b 100644
--- a/src/screens/Profile/Sections/Feed.tsx
+++ b/src/screens/Profile/Sections/Feed.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import {useCallback, useEffect, useImperativeHandle, useState} from 'react'
 import {findNodeHandle, View} from 'react-native'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
@@ -18,6 +18,7 @@ import {Text} from '#/components/Typography'
 import {type SectionRef} from './types'
 
 interface FeedSectionProps {
+  ref?: React.Ref<SectionRef>
   feed: FeedDescriptor
   headerHeight: number
   isFocused: boolean
@@ -25,31 +26,26 @@ interface FeedSectionProps {
   ignoreFilterFor?: string
   setScrollViewTag: (tag: number | null) => void
 }
-export const ProfileFeedSection = React.forwardRef<
-  SectionRef,
-  FeedSectionProps
->(function FeedSectionImpl(
-  {
-    feed,
-    headerHeight,
-    isFocused,
-    scrollElRef,
-    ignoreFilterFor,
-    setScrollViewTag,
-  },
+export function ProfileFeedSection({
   ref,
-) {
+  feed,
+  headerHeight,
+  isFocused,
+  scrollElRef,
+  ignoreFilterFor,
+  setScrollViewTag,
+}: FeedSectionProps) {
   const {_} = useLingui()
   const queryClient = useQueryClient()
-  const [hasNew, setHasNew] = React.useState(false)
-  const [isScrolledDown, setIsScrolledDown] = React.useState(false)
+  const [hasNew, setHasNew] = useState(false)
+  const [isScrolledDown, setIsScrolledDown] = useState(false)
   const shouldUseAdjustedNumToRender = feed.endsWith('posts_and_author_threads')
   const isVideoFeed = isNative && feed.endsWith('posts_with_video')
   const adjustedInitialNumToRender = useInitialNumToRender({
     screenHeightOffset: headerHeight,
   })
 
-  const onScrollToTop = React.useCallback(() => {
+  const onScrollToTop = useCallback(() => {
     scrollElRef.current?.scrollToOffset({
       animated: isNative,
       offset: -headerHeight,
@@ -58,15 +54,15 @@ export const ProfileFeedSection = React.forwardRef<
     setHasNew(false)
   }, [scrollElRef, headerHeight, queryClient, feed, setHasNew])
 
-  React.useImperativeHandle(ref, () => ({
+  useImperativeHandle(ref, () => ({
     scrollToTop: onScrollToTop,
   }))
 
-  const renderPostsEmpty = React.useCallback(() => {
+  const renderPostsEmpty = useCallback(() => {
     return <EmptyState icon="growth" message={_(msg`No posts yet.`)} />
   }, [_])
 
-  React.useEffect(() => {
+  useEffect(() => {
     if (isIOS && isFocused && scrollElRef.current) {
       const nativeTag = findNodeHandle(scrollElRef.current)
       setScrollViewTag(nativeTag)
@@ -101,7 +97,7 @@ export const ProfileFeedSection = React.forwardRef<
       )}
     </View>
   )
-})
+}
 
 function ProfileEndOfFeed() {
   const t = useTheme()
diff --git a/src/screens/Profile/Sections/Labels.tsx b/src/screens/Profile/Sections/Labels.tsx
index 669a5dbcc..120fb8225 100644
--- a/src/screens/Profile/Sections/Labels.tsx
+++ b/src/screens/Profile/Sections/Labels.tsx
@@ -33,6 +33,7 @@ interface LabelsSectionProps {
   isFocused: boolean
   setScrollViewTag: (tag: number | null) => void
 }
+
 export function ProfileLabelsSection({
   ref,
   isLabelerLoading,
diff --git a/src/view/com/feeds/ProfileFeedgens.tsx b/src/view/com/feeds/ProfileFeedgens.tsx
index 5ba17e426..f53a5f5df 100644
--- a/src/view/com/feeds/ProfileFeedgens.tsx
+++ b/src/view/com/feeds/ProfileFeedgens.tsx
@@ -1,8 +1,15 @@
-import React from 'react'
+import {
+  useCallback,
+  useEffect,
+  useImperativeHandle,
+  useMemo,
+  useState,
+} from 'react'
 import {
   findNodeHandle,
   type ListRenderItemInfo,
   type StyleProp,
+  useWindowDimensions,
   View,
   type ViewStyle,
 } from 'react-native'
@@ -34,6 +41,7 @@ interface SectionRef {
 }
 
 interface ProfileFeedgensProps {
+  ref?: React.Ref<SectionRef>
   did: string
   scrollElRef: ListRef
   headerOffset: number
@@ -43,17 +51,21 @@ interface ProfileFeedgensProps {
   setScrollViewTag: (tag: number | null) => void
 }
 
-export const ProfileFeedgens = React.forwardRef<
-  SectionRef,
-  ProfileFeedgensProps
->(function ProfileFeedgensImpl(
-  {did, scrollElRef, headerOffset, enabled, style, testID, setScrollViewTag},
+export function ProfileFeedgens({
   ref,
-) {
+  did,
+  scrollElRef,
+  headerOffset,
+  enabled,
+  style,
+  testID,
+  setScrollViewTag,
+}: ProfileFeedgensProps) {
   const {_} = useLingui()
   const t = useTheme()
-  const [isPTRing, setIsPTRing] = React.useState(false)
-  const opts = React.useMemo(() => ({enabled}), [enabled])
+  const [isPTRing, setIsPTRing] = useState(false)
+  const {height} = useWindowDimensions()
+  const opts = useMemo(() => ({enabled}), [enabled])
   const {
     data,
     isPending,
@@ -67,7 +79,7 @@ export const ProfileFeedgens = React.forwardRef<
   const isEmpty = !isPending && !data?.pages[0]?.feeds.length
   const {data: preferences} = usePreferencesQuery()
 
-  const items = React.useMemo(() => {
+  const items = useMemo(() => {
     let items: any[] = []
     if (isError && isEmpty) {
       items = items.concat([ERROR_ITEM])
@@ -91,7 +103,7 @@ export const ProfileFeedgens = React.forwardRef<
 
   const queryClient = useQueryClient()
 
-  const onScrollToTop = React.useCallback(() => {
+  const onScrollToTop = useCallback(() => {
     scrollElRef.current?.scrollToOffset({
       animated: isNative,
       offset: -headerOffset,
@@ -99,11 +111,11 @@ export const ProfileFeedgens = React.forwardRef<
     queryClient.invalidateQueries({queryKey: RQKEY(did)})
   }, [scrollElRef, queryClient, headerOffset, did])
 
-  React.useImperativeHandle(ref, () => ({
+  useImperativeHandle(ref, () => ({
     scrollToTop: onScrollToTop,
   }))
 
-  const onRefresh = React.useCallback(async () => {
+  const onRefresh = useCallback(async () => {
     setIsPTRing(true)
     try {
       await refetch()
@@ -113,7 +125,7 @@ export const ProfileFeedgens = React.forwardRef<
     setIsPTRing(false)
   }, [refetch, setIsPTRing])
 
-  const onEndReached = React.useCallback(async () => {
+  const onEndReached = useCallback(async () => {
     if (isFetchingNextPage || !hasNextPage || isError) return
 
     try {
@@ -123,14 +135,14 @@ export const ProfileFeedgens = React.forwardRef<
     }
   }, [isFetchingNextPage, hasNextPage, isError, fetchNextPage])
 
-  const onPressRetryLoadMore = React.useCallback(() => {
+  const onPressRetryLoadMore = useCallback(() => {
     fetchNextPage()
   }, [fetchNextPage])
 
   // rendering
   // =
 
-  const renderItem = React.useCallback(
+  const renderItem = useCallback(
     ({item, index}: ListRenderItemInfo<any>) => {
       if (item === EMPTY) {
         return (
@@ -174,14 +186,14 @@ export const ProfileFeedgens = React.forwardRef<
     [_, t, error, refetch, onPressRetryLoadMore, preferences],
   )
 
-  React.useEffect(() => {
+  useEffect(() => {
     if (isIOS && enabled && scrollElRef.current) {
       const nativeTag = findNodeHandle(scrollElRef.current)
       setScrollViewTag(nativeTag)
     }
   }, [enabled, scrollElRef, setScrollViewTag])
 
-  const ProfileFeedgensFooter = React.useCallback(() => {
+  const ProfileFeedgensFooter = useCallback(() => {
     if (isEmpty) return null
     return (
       <ListFooter
@@ -217,10 +229,11 @@ export const ProfileFeedgens = React.forwardRef<
         removeClippedSubviews={true}
         desktopFixedHeight
         onEndReached={onEndReached}
+        contentContainerStyle={{minHeight: height + headerOffset}}
       />
     </View>
   )
-})
+}
 
 function keyExtractor(item: any) {
   return item._reactKey || item.uri
diff --git a/src/view/com/lists/ProfileLists.tsx b/src/view/com/lists/ProfileLists.tsx
index 8fea51081..ce51cb337 100644
--- a/src/view/com/lists/ProfileLists.tsx
+++ b/src/view/com/lists/ProfileLists.tsx
@@ -1,8 +1,15 @@
-import React from 'react'
+import {
+  useCallback,
+  useEffect,
+  useImperativeHandle,
+  useMemo,
+  useState,
+} from 'react'
 import {
   findNodeHandle,
   type ListRenderItemInfo,
   type StyleProp,
+  useWindowDimensions,
   View,
   type ViewStyle,
 } from 'react-native'
@@ -33,6 +40,7 @@ interface SectionRef {
 }
 
 interface ProfileListsProps {
+  ref?: React.Ref<SectionRef>
   did: string
   scrollElRef: ListRef
   headerOffset: number
@@ -42,182 +50,185 @@ interface ProfileListsProps {
   setScrollViewTag: (tag: number | null) => void
 }
 
-export const ProfileLists = React.forwardRef<SectionRef, ProfileListsProps>(
-  function ProfileListsImpl(
-    {did, scrollElRef, headerOffset, enabled, style, testID, setScrollViewTag},
-    ref,
-  ) {
-    const t = useTheme()
-    const {_} = useLingui()
-    const [isPTRing, setIsPTRing] = React.useState(false)
-    const opts = React.useMemo(() => ({enabled}), [enabled])
-    const {
-      data,
-      isPending,
-      hasNextPage,
-      fetchNextPage,
-      isFetchingNextPage,
-      isError,
-      error,
-      refetch,
-    } = useProfileListsQuery(did, opts)
-    const isEmpty = !isPending && !data?.pages[0]?.lists.length
-
-    const items = React.useMemo(() => {
-      let items: any[] = []
-      if (isError && isEmpty) {
-        items = items.concat([ERROR_ITEM])
-      }
-      if (isPending) {
-        items = items.concat([LOADING])
-      } else if (isEmpty) {
-        items = items.concat([EMPTY])
-      } else if (data?.pages) {
-        for (const page of data?.pages) {
-          items = items.concat(page.lists)
-        }
-      }
-      if (isError && !isEmpty) {
-        items = items.concat([LOAD_MORE_ERROR_ITEM])
-      }
-      return items
-    }, [isError, isEmpty, isPending, data])
-
-    // events
-    // =
-
-    const queryClient = useQueryClient()
-
-    const onScrollToTop = React.useCallback(() => {
-      scrollElRef.current?.scrollToOffset({
-        animated: isNative,
-        offset: -headerOffset,
-      })
-      queryClient.invalidateQueries({queryKey: RQKEY(did)})
-    }, [scrollElRef, queryClient, headerOffset, did])
-
-    React.useImperativeHandle(ref, () => ({
-      scrollToTop: onScrollToTop,
-    }))
-
-    const onRefresh = React.useCallback(async () => {
-      setIsPTRing(true)
-      try {
-        await refetch()
-      } catch (err) {
-        logger.error('Failed to refresh lists', {message: err})
+export function ProfileLists({
+  ref,
+  did,
+  scrollElRef,
+  headerOffset,
+  enabled,
+  style,
+  testID,
+  setScrollViewTag,
+}: ProfileListsProps) {
+  const t = useTheme()
+  const {_} = useLingui()
+  const {height} = useWindowDimensions()
+  const [isPTRing, setIsPTRing] = useState(false)
+  const opts = useMemo(() => ({enabled}), [enabled])
+  const {
+    data,
+    isPending,
+    hasNextPage,
+    fetchNextPage,
+    isFetchingNextPage,
+    isError,
+    error,
+    refetch,
+  } = useProfileListsQuery(did, opts)
+  const isEmpty = !isPending && !data?.pages[0]?.lists.length
+
+  const items = useMemo(() => {
+    let items: any[] = []
+    if (isError && isEmpty) {
+      items = items.concat([ERROR_ITEM])
+    }
+    if (isPending) {
+      items = items.concat([LOADING])
+    } else if (isEmpty) {
+      items = items.concat([EMPTY])
+    } else if (data?.pages) {
+      for (const page of data?.pages) {
+        items = items.concat(page.lists)
       }
-      setIsPTRing(false)
-    }, [refetch, setIsPTRing])
-
-    const onEndReached = React.useCallback(async () => {
-      if (isFetchingNextPage || !hasNextPage || isError) return
-      try {
-        await fetchNextPage()
-      } catch (err) {
-        logger.error('Failed to load more lists', {message: err})
-      }
-    }, [isFetchingNextPage, hasNextPage, isError, fetchNextPage])
-
-    const onPressRetryLoadMore = React.useCallback(() => {
-      fetchNextPage()
-    }, [fetchNextPage])
-
-    // rendering
-    // =
-
-    const renderItemInner = React.useCallback(
-      ({item, index}: ListRenderItemInfo<any>) => {
-        if (item === EMPTY) {
-          return (
-            <EmptyState
-              icon="list-ul"
-              message={_(msg`You have no lists.`)}
-              testID="listsEmpty"
-            />
-          )
-        } else if (item === ERROR_ITEM) {
-          return (
-            <ErrorMessage
-              message={cleanError(error)}
-              onPressTryAgain={refetch}
-            />
-          )
-        } else if (item === LOAD_MORE_ERROR_ITEM) {
-          return (
-            <LoadMoreRetryBtn
-              label={_(
-                msg`There was an issue fetching your lists. Tap here to try again.`,
-              )}
-              onPress={onPressRetryLoadMore}
-            />
-          )
-        } else if (item === LOADING) {
-          return <FeedLoadingPlaceholder />
-        }
+    }
+    if (isError && !isEmpty) {
+      items = items.concat([LOAD_MORE_ERROR_ITEM])
+    }
+    return items
+  }, [isError, isEmpty, isPending, data])
+
+  // events
+  // =
+
+  const queryClient = useQueryClient()
+
+  const onScrollToTop = useCallback(() => {
+    scrollElRef.current?.scrollToOffset({
+      animated: isNative,
+      offset: -headerOffset,
+    })
+    queryClient.invalidateQueries({queryKey: RQKEY(did)})
+  }, [scrollElRef, queryClient, headerOffset, did])
+
+  useImperativeHandle(ref, () => ({
+    scrollToTop: onScrollToTop,
+  }))
+
+  const onRefresh = useCallback(async () => {
+    setIsPTRing(true)
+    try {
+      await refetch()
+    } catch (err) {
+      logger.error('Failed to refresh lists', {message: err})
+    }
+    setIsPTRing(false)
+  }, [refetch, setIsPTRing])
+
+  const onEndReached = useCallback(async () => {
+    if (isFetchingNextPage || !hasNextPage || isError) return
+    try {
+      await fetchNextPage()
+    } catch (err) {
+      logger.error('Failed to load more lists', {message: err})
+    }
+  }, [isFetchingNextPage, hasNextPage, isError, fetchNextPage])
+
+  const onPressRetryLoadMore = useCallback(() => {
+    fetchNextPage()
+  }, [fetchNextPage])
+
+  // rendering
+  // =
+
+  const renderItemInner = useCallback(
+    ({item, index}: ListRenderItemInfo<any>) => {
+      if (item === EMPTY) {
         return (
-          <View
-            style={[
-              (index !== 0 || isWeb) && a.border_t,
-              t.atoms.border_contrast_low,
-              a.px_lg,
-              a.py_lg,
-            ]}>
-            <ListCard.Default view={item} />
-          </View>
+          <EmptyState
+            icon="list-ul"
+            message={_(msg`You have no lists.`)}
+            testID="listsEmpty"
+          />
         )
-      },
-      [error, refetch, onPressRetryLoadMore, _, t.atoms.border_contrast_low],
-    )
-
-    React.useEffect(() => {
-      if (isIOS && enabled && scrollElRef.current) {
-        const nativeTag = findNodeHandle(scrollElRef.current)
-        setScrollViewTag(nativeTag)
+      } else if (item === ERROR_ITEM) {
+        return (
+          <ErrorMessage message={cleanError(error)} onPressTryAgain={refetch} />
+        )
+      } else if (item === LOAD_MORE_ERROR_ITEM) {
+        return (
+          <LoadMoreRetryBtn
+            label={_(
+              msg`There was an issue fetching your lists. Tap here to try again.`,
+            )}
+            onPress={onPressRetryLoadMore}
+          />
+        )
+      } else if (item === LOADING) {
+        return <FeedLoadingPlaceholder />
       }
-    }, [enabled, scrollElRef, setScrollViewTag])
-
-    const ProfileListsFooter = React.useCallback(() => {
-      if (isEmpty) return null
       return (
-        <ListFooter
-          hasNextPage={hasNextPage}
-          isFetchingNextPage={isFetchingNextPage}
-          onRetry={fetchNextPage}
-          error={cleanError(error)}
-          height={180 + headerOffset}
-        />
+        <View
+          style={[
+            (index !== 0 || isWeb) && a.border_t,
+            t.atoms.border_contrast_low,
+            a.px_lg,
+            a.py_lg,
+          ]}>
+          <ListCard.Default view={item} />
+        </View>
       )
-    }, [
-      hasNextPage,
-      error,
-      isFetchingNextPage,
-      headerOffset,
-      fetchNextPage,
-      isEmpty,
-    ])
-
+    },
+    [error, refetch, onPressRetryLoadMore, _, t.atoms.border_contrast_low],
+  )
+
+  useEffect(() => {
+    if (isIOS && enabled && scrollElRef.current) {
+      const nativeTag = findNodeHandle(scrollElRef.current)
+      setScrollViewTag(nativeTag)
+    }
+  }, [enabled, scrollElRef, setScrollViewTag])
+
+  const ProfileListsFooter = useCallback(() => {
+    if (isEmpty) return null
     return (
-      <View testID={testID} style={style}>
-        <List
-          testID={testID ? `${testID}-flatlist` : undefined}
-          ref={scrollElRef}
-          data={items}
-          keyExtractor={keyExtractor}
-          renderItem={renderItemInner}
-          ListFooterComponent={ProfileListsFooter}
-          refreshing={isPTRing}
-          onRefresh={onRefresh}
-          headerOffset={headerOffset}
-          progressViewOffset={ios(0)}
-          removeClippedSubviews={true}
-          desktopFixedHeight
-          onEndReached={onEndReached}
-        />
-      </View>
+      <ListFooter
+        hasNextPage={hasNextPage}
+        isFetchingNextPage={isFetchingNextPage}
+        onRetry={fetchNextPage}
+        error={cleanError(error)}
+        height={180 + headerOffset}
+      />
     )
-  },
-)
+  }, [
+    hasNextPage,
+    error,
+    isFetchingNextPage,
+    headerOffset,
+    fetchNextPage,
+    isEmpty,
+  ])
+
+  return (
+    <View testID={testID} style={style}>
+      <List
+        testID={testID ? `${testID}-flatlist` : undefined}
+        ref={scrollElRef}
+        data={items}
+        keyExtractor={keyExtractor}
+        renderItem={renderItemInner}
+        ListFooterComponent={ProfileListsFooter}
+        refreshing={isPTRing}
+        onRefresh={onRefresh}
+        headerOffset={headerOffset}
+        progressViewOffset={ios(0)}
+        removeClippedSubviews={true}
+        desktopFixedHeight
+        onEndReached={onEndReached}
+        contentContainerStyle={{minHeight: height + headerOffset}}
+      />
+    </View>
+  )
+}
 
 function keyExtractor(item: any) {
   return item._reactKey || item.uri