From 371cabfabcf5f58d0f53dbc1221fc259f5f93cd1 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 22 Oct 2024 21:06:46 +0100 Subject: [PATCH] New empty state for feeds --- src/view/com/feeds/ProfileFeedgens.tsx | 201 +++++++++++++++++++++++-- src/view/screens/Profile.tsx | 1 + 2 files changed, 189 insertions(+), 13 deletions(-) diff --git a/src/view/com/feeds/ProfileFeedgens.tsx b/src/view/com/feeds/ProfileFeedgens.tsx index 693a8e361a..1ae9db8796 100644 --- a/src/view/com/feeds/ProfileFeedgens.tsx +++ b/src/view/com/feeds/ProfileFeedgens.tsx @@ -7,24 +7,33 @@ import { ViewStyle, } from 'react-native' import {msg} from '@lingui/macro' +import {Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {useNavigation} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' +import {useGenerateStarterPackMutation} from '#/lib/generate-starterpack' +import {NavigationProp} from '#/lib/routes/types' import {cleanError} from '#/lib/strings/errors' +import {parseStarterPackUri} from '#/lib/strings/starter-pack' import {logger} from '#/logger' import {isNative, isWeb} from '#/platform/detection' import {usePreferencesQuery} from '#/state/queries/preferences' import {RQKEY, useProfileFeedgensQuery} from '#/state/queries/profile-feedgens' -import {EmptyState} from '#/view/com/util/EmptyState' import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' +import {Text} from '#/view/com/util/text/Text' import {atoms as a, ios, useTheme} from '#/alf' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import {useDialogControl} from '#/components/Dialog' import * as FeedCard from '#/components/FeedCard' +import {PlusSmall_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' +import {LinearGradientBackground} from '#/components/LinearGradientBackground' +import * as Prompt from '#/components/Prompt' import {ErrorMessage} from '../util/error/ErrorMessage' import {List, ListRef} from '../util/List' import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn' const LOADING = {_reactKey: '__loading__'} -const EMPTY = {_reactKey: '__empty__'} const ERROR_ITEM = {_reactKey: '__error__'} const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'} @@ -40,13 +49,23 @@ interface ProfileFeedgensProps { style?: StyleProp testID?: string setScrollViewTag: (tag: number | null) => void + isMe: boolean } export const ProfileFeedgens = React.forwardRef< SectionRef, ProfileFeedgensProps >(function ProfileFeedgensImpl( - {did, scrollElRef, headerOffset, enabled, style, testID, setScrollViewTag}, + { + did, + scrollElRef, + headerOffset, + enabled, + style, + testID, + setScrollViewTag, + isMe, + }, ref, ) { const {_} = useLingui() @@ -74,7 +93,7 @@ export const ProfileFeedgens = React.forwardRef< if (!isFetched && isFetching) { items = items.concat([LOADING]) } else if (isEmpty) { - items = items.concat([EMPTY]) + // -- handled separately -- } else if (data?.pages) { for (const page of data?.pages) { items = items.concat(page.feeds) @@ -131,15 +150,7 @@ export const ProfileFeedgens = React.forwardRef< const renderItem = React.useCallback( ({item, index}: ListRenderItemInfo) => { - if (item === EMPTY) { - return ( - - ) - } else if (item === ERROR_ITEM) { + if (item === ERROR_ITEM) { return ( ) @@ -198,7 +209,171 @@ export const ProfileFeedgens = React.forwardRef< // @ts-ignore our .web version only -prf desktopFixedHeight onEndReached={onEndReached} + ListEmptyComponent={Empty} + ListFooterComponent={ + items?.length !== 0 && isMe ? CreateAnother : undefined + } /> ) }) + +function CreateAnother() { + const {_} = useLingui() + const t = useTheme() + const navigation = useNavigation() + + return ( + + + + ) +} + +function Empty() { + const {_} = useLingui() + const t = useTheme() + const navigation = useNavigation() + const confirmDialogControl = useDialogControl() + const followersDialogControl = useDialogControl() + const errorDialogControl = useDialogControl() + + const [isGenerating, setIsGenerating] = React.useState(false) + + const {mutate: generateStarterPack} = useGenerateStarterPackMutation({ + onSuccess: ({uri}) => { + const parsed = parseStarterPackUri(uri) + if (parsed) { + navigation.push('StarterPack', { + name: parsed.name, + rkey: parsed.rkey, + }) + } + setIsGenerating(false) + }, + onError: e => { + logger.error('Failed to generate starter pack', {safeMessage: e}) + setIsGenerating(false) + if (e.name === 'NOT_ENOUGH_FOLLOWERS') { + followersDialogControl.open() + } else { + errorDialogControl.open() + } + }, + }) + + const generate = () => { + setIsGenerating(true) + generateStarterPack() + } + + return ( + + + + You haven't created a feed yet! + + + + Create a feed to curate your own and others people's posts. + + + + + + + + + + Generate a starter pack + + + + Bluesky will choose a set of recommended accounts from people in + your network. + + + + + { + navigation.navigate('StarterPackWizard') + }} + /> + + + {}} + showCancel={false} + /> + + + ) +} diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx index 677fe09f47..3a100bbb7e 100644 --- a/src/view/screens/Profile.tsx +++ b/src/view/screens/Profile.tsx @@ -453,6 +453,7 @@ function ProfileScreenLoaded({ ? ({headerHeight, isFocused, scrollElRef}) => (