about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/view/com/lists/ListMembers.tsx35
-rw-r--r--src/view/com/util/EmptyState.tsx4
-rw-r--r--src/view/screens/ProfileList.tsx123
3 files changed, 98 insertions, 64 deletions
diff --git a/src/view/com/lists/ListMembers.tsx b/src/view/com/lists/ListMembers.tsx
index cf7bb6b9e..0caae6701 100644
--- a/src/view/com/lists/ListMembers.tsx
+++ b/src/view/com/lists/ListMembers.tsx
@@ -1,11 +1,5 @@
-import React from 'react'
-import {
-  ActivityIndicator,
-  Dimensions,
-  StyleProp,
-  View,
-  ViewStyle,
-} from 'react-native'
+import React, {useCallback} from 'react'
+import {Dimensions, StyleProp, View, ViewStyle} from 'react-native'
 import {AppBskyActorDefs, AppBskyGraphDefs} from '@atproto/api'
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
@@ -16,6 +10,7 @@ import {logger} from '#/logger'
 import {useModalControls} from '#/state/modals'
 import {useListMembersQuery} from '#/state/queries/list-members'
 import {useSession} from '#/state/session'
+import {ListFooter} from '#/components/Lists'
 import {ProfileCard} from '../profile/ProfileCard'
 import {ErrorMessage} from '../util/error/ErrorMessage'
 import {Button} from '../util/forms/Button'
@@ -66,6 +61,7 @@ export function ListMembers({
     refetch,
     fetchNextPage,
     hasNextPage,
+    isFetchingNextPage,
   } = useListMembersQuery(list)
   const isEmpty = !isFetching && !data?.pages[0].items.length
   const isOwner =
@@ -197,14 +193,17 @@ export function ListMembers({
     ],
   )
 
-  const Footer = React.useCallback(
-    () => (
-      <View style={{paddingTop: 20, paddingBottom: 400}}>
-        {isFetching && <ActivityIndicator />}
-      </View>
-    ),
-    [isFetching],
-  )
+  const renderFooter = useCallback(() => {
+    if (isEmpty) return null
+    return (
+      <ListFooter
+        hasNextPage={hasNextPage}
+        error={cleanError(error)}
+        isFetchingNextPage={isFetchingNextPage}
+        onRetry={fetchNextPage}
+      />
+    )
+  }, [hasNextPage, error, isFetchingNextPage, fetchNextPage, isEmpty])
 
   return (
     <View testID={testID} style={style}>
@@ -214,8 +213,8 @@ export function ListMembers({
         data={items}
         keyExtractor={(item: any) => item.subject?.did || item._reactKey}
         renderItem={renderItem}
-        ListHeaderComponent={renderHeader}
-        ListFooterComponent={Footer}
+        ListHeaderComponent={!isEmpty ? renderHeader : undefined}
+        ListFooterComponent={renderFooter}
         refreshing={isRefreshing}
         onRefresh={onRefresh}
         headerOffset={headerOffset}
diff --git a/src/view/com/util/EmptyState.tsx b/src/view/com/util/EmptyState.tsx
index a6352c2fe..6b2600a40 100644
--- a/src/view/com/util/EmptyState.tsx
+++ b/src/view/com/util/EmptyState.tsx
@@ -26,9 +26,7 @@ export function EmptyState({
   const {isTabletOrDesktop} = useWebMediaQueries()
   const iconSize = isTabletOrDesktop ? 64 : 48
   return (
-    <View
-      testID={testID}
-      style={[isTabletOrDesktop && {paddingRight: 20}, style]}>
+    <View testID={testID} style={style}>
       <View
         style={[
           styles.iconContainer,
diff --git a/src/view/screens/ProfileList.tsx b/src/view/screens/ProfileList.tsx
index 2e661ff46..788f41c55 100644
--- a/src/view/screens/ProfileList.tsx
+++ b/src/view/screens/ProfileList.tsx
@@ -1,5 +1,5 @@
 import React, {useCallback, useMemo} from 'react'
-import {Pressable, StyleSheet, View} from 'react-native'
+import {StyleSheet, View} from 'react-native'
 import {useAnimatedRef} from 'react-native-reanimated'
 import {
   AppBskyGraphDefs,
@@ -70,7 +70,9 @@ import {Text} from '#/view/com/util/text/Text'
 import * as Toast from '#/view/com/util/Toast'
 import {ListHiddenScreen} from '#/screens/List/ListHiddenScreen'
 import {atoms as a} from '#/alf'
+import {Button as NewButton, ButtonIcon, ButtonText} from '#/components/Button'
 import {useDialogControl} from '#/components/Dialog'
+import {PersonPlus_Stroke2_Corner0_Rounded as PersonPlusIcon} from '#/components/icons/Person'
 import * as Layout from '#/components/Layout'
 import * as Hider from '#/components/moderation/Hider'
 import * as Prompt from '#/components/Prompt'
@@ -220,6 +222,7 @@ function ProfileListScreenLoaded({
                   scrollElRef={scrollElRef as ListRef}
                   headerHeight={headerHeight}
                   isFocused={isScreenFocused && isFocused}
+                  onPressAddUser={onPressAddUser}
                 />
               )}
               {({headerHeight, scrollElRef}) => (
@@ -771,9 +774,13 @@ interface FeedSectionProps {
   headerHeight: number
   scrollElRef: ListRef
   isFocused: boolean
+  onPressAddUser: () => void
 }
 const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
-  function FeedSectionImpl({feed, scrollElRef, headerHeight, isFocused}, ref) {
+  function FeedSectionImpl(
+    {feed, scrollElRef, headerHeight, isFocused, onPressAddUser},
+    ref,
+  ) {
     const queryClient = useQueryClient()
     const [hasNew, setHasNew] = React.useState(false)
     const [isScrolledDown, setIsScrolledDown] = React.useState(false)
@@ -800,8 +807,23 @@ const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>(
     }, [onScrollToTop, isScreenFocused])
 
     const renderPostsEmpty = useCallback(() => {
-      return <EmptyState icon="hashtag" message={_(msg`This feed is empty.`)} />
-    }, [_])
+      return (
+        <View style={[a.gap_xl, a.align_center]}>
+          <EmptyState icon="hashtag" message={_(msg`This feed is empty.`)} />
+          <NewButton
+            label={_(msg`Start adding people`)}
+            onPress={onPressAddUser}
+            color="primary"
+            size="small"
+            variant="solid">
+            <ButtonIcon icon={PersonPlusIcon} />
+            <ButtonText>
+              <Trans>Start adding people!</Trans>
+            </ButtonText>
+          </NewButton>
+        </View>
+      )
+    }, [_, onPressAddUser])
 
     return (
       <View>
@@ -840,10 +862,9 @@ const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>(
     {list, onPressAddUser, headerHeight, scrollElRef},
     ref,
   ) {
-    const pal = usePalette('default')
     const {_} = useLingui()
-    const {isMobile} = useWebMediaQueries()
     const {currentAccount} = useSession()
+    const {isMobile} = useWebMediaQueries()
     const [isScrolledDown, setIsScrolledDown] = React.useState(false)
     const isOwner = list.creator.did === currentAccount?.did
 
@@ -862,50 +883,66 @@ const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>(
       if (!isOwner) {
         return <View />
       }
-      return (
-        <View style={a.pt_lg}>
-          <View
-            style={[
-              {
-                flexDirection: 'row',
-                alignItems: 'center',
-                justifyContent: 'space-between',
-                paddingHorizontal: isMobile ? 14 : 20,
-                paddingBottom: isMobile ? 14 : 18,
-              },
-            ]}>
-            {isOwner && (
-              <Pressable
-                testID="addUserBtn"
-                accessibilityRole="button"
-                accessibilityLabel={_(msg`Add a user to this list`)}
-                accessibilityHint=""
-                onPress={onPressAddUser}
-                style={{flexDirection: 'row', alignItems: 'center', gap: 6}}>
-                <FontAwesomeIcon
-                  icon="user-plus"
-                  color={pal.colors.link}
-                  size={16}
-                />
-                <Text style={pal.link}>
-                  <Trans>Add</Trans>
-                </Text>
-              </Pressable>
-            )}
+      if (isMobile) {
+        return (
+          <View style={[a.px_sm, a.py_sm]}>
+            <NewButton
+              testID="addUserBtn"
+              label={_(msg`Add a user to this list`)}
+              onPress={onPressAddUser}
+              color="primary"
+              size="small"
+              variant="outline"
+              style={[a.py_md]}>
+              <ButtonIcon icon={PersonPlusIcon} />
+              <ButtonText>
+                <Trans>Add people</Trans>
+              </ButtonText>
+            </NewButton>
           </View>
+        )
+      }
+      return (
+        <View style={[a.px_lg, a.py_md, a.flex_row_reverse]}>
+          <NewButton
+            testID="addUserBtn"
+            label={_(msg`Add a user to this list`)}
+            onPress={onPressAddUser}
+            color="primary"
+            size="small"
+            variant="ghost"
+            style={[a.py_sm]}>
+            <ButtonIcon icon={PersonPlusIcon} />
+            <ButtonText>
+              <Trans>Add people</Trans>
+            </ButtonText>
+          </NewButton>
         </View>
       )
-    }, [isMobile, pal.colors.link, pal.link, isOwner, _, onPressAddUser])
+    }, [isOwner, _, onPressAddUser, isMobile])
 
     const renderEmptyState = useCallback(() => {
       return (
-        <EmptyState
-          icon="users-slash"
-          message={_(msg`This list is empty!`)}
-          style={{paddingTop: 40}}
-        />
+        <View style={[a.gap_xl, a.align_center]}>
+          <EmptyState
+            icon="users-slash"
+            message={_(msg`This list is empty.`)}
+          />
+          <NewButton
+            testID="emptyStateAddUserBtn"
+            label={_(msg`Start adding people`)}
+            onPress={onPressAddUser}
+            color="primary"
+            size="small"
+            variant="solid">
+            <ButtonIcon icon={PersonPlusIcon} />
+            <ButtonText>
+              <Trans>Start adding people!</Trans>
+            </ButtonText>
+          </NewButton>
+        </View>
       )
-    }, [_])
+    }, [_, onPressAddUser])
 
     return (
       <View>