Onboarding tweaks (#272)

* Small fix to side menu rendering

* Change onboarding to use an explicit 'is onboarding' mode to more clearly control the flow

* Add a progress bar to the welcome banner

* Dont show the 'unfollow button' on posts in weird times (close #271)

* Improve the empty state of the feed

* Only suggest recent posts
This commit is contained in:
Paul Frazee 2023-03-06 15:34:22 -06:00 committed by GitHub
parent 74c30c60b8
commit 36791e68b3
13 changed files with 259 additions and 123 deletions

View file

@ -7,26 +7,28 @@ import {
StyleSheet,
ViewStyle,
} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome'
import {CenteredView, FlatList} from '../util/Views'
import {PostFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
import {EmptyState} from '../util/EmptyState'
import {Text} from '../util/text/Text'
import {ErrorMessage} from '../util/error/ErrorMessage'
import {Button} from '../util/forms/Button'
import {FeedModel} from 'state/models/feed-view'
import {FeedItem} from './FeedItem'
import {WelcomeBanner} from '../util/WelcomeBanner'
import {OnScrollCb} from 'lib/hooks/useOnMainScroll'
import {s} from 'lib/styles'
import {useAnalytics} from 'lib/analytics'
import {useStores} from 'state/index'
import {useAnalytics} from 'lib/analytics'
import {usePalette} from 'lib/hooks/usePalette'
import {MagnifyingGlassIcon} from 'lib/icons'
const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
const ERROR_FEED_ITEM = {_reactKey: '__error__'}
const WELCOME_FEED_ITEM = {_reactKey: '__welcome__'}
export const Feed = observer(function Feed({
feed,
style,
showWelcomeBanner,
showPostFollowBtn,
scrollElRef,
onPressTryAgain,
@ -36,7 +38,6 @@ export const Feed = observer(function Feed({
}: {
feed: FeedModel
style?: StyleProp<ViewStyle>
showWelcomeBanner?: boolean
showPostFollowBtn?: boolean
scrollElRef?: MutableRefObject<FlatList<any> | null>
onPressTryAgain?: () => void
@ -44,10 +45,11 @@ export const Feed = observer(function Feed({
testID?: string
headerOffset?: number
}) {
const {track} = useAnalytics()
const pal = usePalette('default')
const palInverted = usePalette('inverted')
const store = useStores()
const {track} = useAnalytics()
const [isRefreshing, setIsRefreshing] = React.useState(false)
const [isNewUser, setIsNewUser] = React.useState<boolean>(false)
const data = React.useMemo(() => {
let feedItems: any[] = []
@ -55,9 +57,6 @@ export const Feed = observer(function Feed({
if (feed.hasError) {
feedItems = feedItems.concat([ERROR_FEED_ITEM])
}
if (showWelcomeBanner && isNewUser) {
feedItems = feedItems.concat([WELCOME_FEED_ITEM])
}
if (feed.isEmpty) {
feedItems = feedItems.concat([EMPTY_FEED_ITEM])
} else {
@ -65,39 +64,21 @@ export const Feed = observer(function Feed({
}
}
return feedItems
}, [
feed.hasError,
feed.hasLoaded,
feed.isEmpty,
feed.nonReplyFeed,
showWelcomeBanner,
isNewUser,
])
}, [feed.hasError, feed.hasLoaded, feed.isEmpty, feed.nonReplyFeed])
// events
// =
const checkWelcome = React.useCallback(async () => {
if (showWelcomeBanner && store.me.did) {
await store.me.follows.fetchIfNeeded()
setIsNewUser(store.me.follows.isEmpty)
}
}, [showWelcomeBanner, store.me.follows, store.me.did])
React.useEffect(() => {
checkWelcome()
}, [checkWelcome])
const onRefresh = React.useCallback(async () => {
track('Feed:onRefresh')
setIsRefreshing(true)
checkWelcome()
try {
await feed.refresh()
} catch (err) {
feed.rootStore.log.error('Failed to refresh posts feed', err)
}
setIsRefreshing(false)
}, [feed, track, setIsRefreshing, checkWelcome])
}, [feed, track, setIsRefreshing])
const onEndReached = React.useCallback(async () => {
track('Feed:onEndReached')
try {
@ -118,11 +99,30 @@ export const Feed = observer(function Feed({
({item}: {item: any}) => {
if (item === EMPTY_FEED_ITEM) {
return (
<EmptyState
icon="bars"
message="This feed is empty!"
style={styles.emptyState}
/>
<View style={styles.emptyContainer}>
<View style={styles.emptyIconContainer}>
<MagnifyingGlassIcon
style={[styles.emptyIcon, pal.text]}
size={62}
/>
</View>
<Text type="xl-medium" style={[s.textCenter, pal.text]}>
Your feed is empty! You should follow some accounts to fix this.
</Text>
<Button
type="inverted"
style={styles.emptyBtn}
onPress={() => store.nav.navigate('/search')}>
<Text type="lg-medium" style={palInverted.text}>
Find accounts
</Text>
<FontAwesomeIcon
icon="angle-right"
style={palInverted.text as FontAwesomeIconStyle}
size={14}
/>
</Button>
</View>
)
} else if (item === ERROR_FEED_ITEM) {
return (
@ -131,12 +131,10 @@ export const Feed = observer(function Feed({
onPressTryAgain={onPressTryAgain}
/>
)
} else if (item === WELCOME_FEED_ITEM) {
return <WelcomeBanner />
}
return <FeedItem item={item} showFollowBtn={showPostFollowBtn} />
},
[feed, onPressTryAgain, showPostFollowBtn],
[feed, onPressTryAgain, showPostFollowBtn, pal, palInverted, store.nav],
)
const FeedFooter = React.useCallback(
@ -155,7 +153,6 @@ export const Feed = observer(function Feed({
<View testID={testID} style={style}>
{feed.isLoading && data.length === 0 && (
<CenteredView style={{paddingTop: headerOffset}}>
{showWelcomeBanner && isNewUser && <WelcomeBanner />}
<PostFeedLoadingPlaceholder />
</CenteredView>
)}
@ -184,5 +181,21 @@ export const Feed = observer(function Feed({
const styles = StyleSheet.create({
feedFooter: {paddingTop: 20},
emptyState: {paddingVertical: 40},
emptyContainer: {
paddingVertical: 40,
paddingHorizontal: 30,
},
emptyIconContainer: {
marginBottom: 16,
},
emptyIcon: {
marginLeft: 'auto',
marginRight: 'auto',
},
emptyBtn: {
marginTop: 20,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
})