New onboarding (#241)

* delete old onboarding files and code

* add custom FollowButton component to Post, FeedItem, & ProfileCard

* move building suggested feed into helper lib

* show suggested posts/feed if follower list is empty

* Update tsconfig.json

* add pagination to getting new onboarding

* remove unnecessary console log

* fix naming, add better null check for combinedCursor

* In locally-combined feeds, correctly produce an undefined cursor when out of data

* Minor refactors of the suggested posts lib functions

* Show 'follow button' style of post meta in certain conditions only

* Only show follow btn in posts on the main feed and the discovery feed

* Add a welcome notice to the home feed

* Tune the timing of when the welcome banner shows or hides

* Make the follow button an observer (closes #244)

* Update postmeta to keep the follow btn after press until next render

* A couple of fixes that ensure consistent welcome screen

* Fix lint

* Rework the welcome banner

* Fix cache invalidation of follows model on user switch

* Show welcome banner while loading

* Update the home onboarding feed to get top posts from hardcode recommends

* Drop unused helper function

* Update happy path tests

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
This commit is contained in:
Ansh 2023-03-02 10:21:33 -08:00 committed by GitHub
parent 9b46b2e6a9
commit bd9386d81c
31 changed files with 426 additions and 866 deletions

View file

@ -13,16 +13,21 @@ import {EmptyState} from '../util/EmptyState'
import {ErrorMessage} from '../util/error/ErrorMessage'
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'
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,
onScroll,
@ -31,6 +36,8 @@ export const Feed = observer(function Feed({
}: {
feed: FeedModel
style?: StyleProp<ViewStyle>
showWelcomeBanner?: boolean
showPostFollowBtn?: boolean
scrollElRef?: MutableRefObject<FlatList<any> | null>
onPressTryAgain?: () => void
onScroll?: OnScrollCb
@ -38,7 +45,9 @@ export const Feed = observer(function Feed({
headerOffset?: number
}) {
const {track} = useAnalytics()
const store = useStores()
const [isRefreshing, setIsRefreshing] = React.useState(false)
const [isNewUser, setIsNewUser] = React.useState<boolean>(false)
const data = React.useMemo(() => {
let feedItems: any[] = []
@ -46,6 +55,9 @@ 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 {
@ -53,21 +65,39 @@ export const Feed = observer(function Feed({
}
}
return feedItems
}, [feed.hasError, feed.hasLoaded, feed.isEmpty, feed.feed])
}, [
feed.hasError,
feed.hasLoaded,
feed.isEmpty,
feed.feed,
showWelcomeBanner,
isNewUser,
])
// events
// =
const checkWelcome = React.useCallback(async () => {
if (showWelcomeBanner) {
await store.me.follows.fetchIfNeeded()
setIsNewUser(store.me.follows.isEmpty)
}
}, [showWelcomeBanner, store.me.follows])
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])
}, [feed, track, setIsRefreshing, checkWelcome])
const onEndReached = React.useCallback(async () => {
track('Feed:onEndReached')
try {
@ -101,10 +131,12 @@ export const Feed = observer(function Feed({
onPressTryAgain={onPressTryAgain}
/>
)
} else if (item === WELCOME_FEED_ITEM) {
return <WelcomeBanner />
}
return <FeedItem item={item} />
return <FeedItem item={item} showFollowBtn={showPostFollowBtn} />
},
[feed, onPressTryAgain],
[feed, onPressTryAgain, showPostFollowBtn],
)
const FeedFooter = React.useCallback(
@ -123,6 +155,7 @@ 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>
)}

View file

@ -26,10 +26,12 @@ import {useAnalytics} from 'lib/analytics'
export const FeedItem = observer(function ({
item,
showReplyLine,
showFollowBtn,
ignoreMuteFor,
}: {
item: FeedItemModel
showReplyLine?: boolean
showFollowBtn?: boolean
ignoreMuteFor?: string
}) {
const store = useStores()
@ -175,6 +177,9 @@ export const FeedItem = observer(function ({
authorHandle={item.post.author.handle}
authorDisplayName={item.post.author.displayName}
timestamp={item.post.indexedAt}
did={item.post.author.did}
declarationCid={item.post.author.declaration.cid}
showFollowBtn={showFollowBtn}
/>
{!isChild && replyAuthorDid !== '' && (
<View style={[s.flexRow, s.mb2, s.alignCenter]}>