Handle home algo with backwards compat
This commit is contained in:
@@ -89,3 +89,6 @@ export const BSKY_FEED_OWNER_DIDS = [
|
|||||||
'did:plc:vpkhqolt662uhesyj6nxm7ys',
|
'did:plc:vpkhqolt662uhesyj6nxm7ys',
|
||||||
'did:plc:q6gjnaw2blty4crticxkmujt',
|
'did:plc:q6gjnaw2blty4crticxkmujt',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
export const DISCOVER_FEED_URI =
|
||||||
|
'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot'
|
||||||
|
|||||||
@@ -8,5 +8,5 @@ export type Gate =
|
|||||||
| 'new_search'
|
| 'new_search'
|
||||||
| 'receive_updates'
|
| 'receive_updates'
|
||||||
| 'show_follow_back_label'
|
| 'show_follow_back_label'
|
||||||
| 'start_session_with_following'
|
|
||||||
| 'use_new_suggestions_endpoint'
|
| 'use_new_suggestions_endpoint'
|
||||||
|
| 'reduced_onboarding_and_home_algo'
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import {
|
|||||||
useQuery,
|
useQuery,
|
||||||
} from '@tanstack/react-query'
|
} from '@tanstack/react-query'
|
||||||
|
|
||||||
|
import {DISCOVER_FEED_URI} from '#/lib/constants'
|
||||||
|
import {useGate} from '#/lib/statsig/statsig'
|
||||||
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
||||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||||
import {STALE} from '#/state/queries'
|
import {STALE} from '#/state/queries'
|
||||||
@@ -199,10 +201,13 @@ export function useSearchPopularFeedsMutation() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const FOLLOWING_FEED_STUB: FeedSourceInfo = {
|
/**
|
||||||
|
* The following feed, with fallbacks to Discover
|
||||||
|
*/
|
||||||
|
const HOME_FEED_STUB: FeedSourceInfo = {
|
||||||
type: 'feed',
|
type: 'feed',
|
||||||
displayName: 'Following',
|
displayName: 'Following',
|
||||||
uri: '',
|
uri: 'home',
|
||||||
route: {
|
route: {
|
||||||
href: '/',
|
href: '/',
|
||||||
name: 'Home',
|
name: 'Home',
|
||||||
@@ -219,7 +224,24 @@ const FOLLOWING_FEED_STUB: FeedSourceInfo = {
|
|||||||
const DISCOVER_FEED_STUB: FeedSourceInfo = {
|
const DISCOVER_FEED_STUB: FeedSourceInfo = {
|
||||||
type: 'feed',
|
type: 'feed',
|
||||||
displayName: 'Discover',
|
displayName: 'Discover',
|
||||||
uri: '',
|
uri: DISCOVER_FEED_URI, // TODO may not want this
|
||||||
|
route: {
|
||||||
|
href: '/',
|
||||||
|
name: 'Home',
|
||||||
|
params: {},
|
||||||
|
},
|
||||||
|
cid: '',
|
||||||
|
avatar: '',
|
||||||
|
description: new RichText({text: ''}),
|
||||||
|
creatorDid: '',
|
||||||
|
creatorHandle: '',
|
||||||
|
likeCount: 0,
|
||||||
|
likeUri: '',
|
||||||
|
}
|
||||||
|
const HOME_ALGO_FEED_STUB: FeedSourceInfo = {
|
||||||
|
type: 'feed',
|
||||||
|
displayName: 'Home',
|
||||||
|
uri: 'home-algo',
|
||||||
route: {
|
route: {
|
||||||
href: '/',
|
href: '/',
|
||||||
name: 'Home',
|
name: 'Home',
|
||||||
@@ -239,7 +261,17 @@ const pinnedFeedInfosQueryKeyRoot = 'pinnedFeedsInfos'
|
|||||||
export function usePinnedFeedsInfos() {
|
export function usePinnedFeedsInfos() {
|
||||||
const {hasSession} = useSession()
|
const {hasSession} = useSession()
|
||||||
const {data: preferences, isLoading: isLoadingPrefs} = usePreferencesQuery()
|
const {data: preferences, isLoading: isLoadingPrefs} = usePreferencesQuery()
|
||||||
const pinnedUris = preferences?.feeds?.pinned ?? []
|
const isHomeAlgoExperimentEnabled = useGate(
|
||||||
|
'reduced_onboarding_and_home_algo',
|
||||||
|
)
|
||||||
|
const homeAlgo = preferences?.homeAlgo
|
||||||
|
const pinnedUris = (preferences?.feeds?.pinned ?? []).filter(f => {
|
||||||
|
if (isHomeAlgoExperimentEnabled && hasSession && homeAlgo) {
|
||||||
|
// remove duplicate feed
|
||||||
|
return f !== homeAlgo.uri
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
return useQuery({
|
return useQuery({
|
||||||
staleTime: STALE.INFINITY,
|
staleTime: STALE.INFINITY,
|
||||||
@@ -285,7 +317,15 @@ export function usePinnedFeedsInfos() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// The returned result will have the original order.
|
// The returned result will have the original order.
|
||||||
const result = [hasSession ? FOLLOWING_FEED_STUB : DISCOVER_FEED_STUB]
|
let result = [hasSession ? HOME_FEED_STUB : DISCOVER_FEED_STUB]
|
||||||
|
|
||||||
|
if (isHomeAlgoExperimentEnabled && hasSession && preferences?.homeAlgo) {
|
||||||
|
const {enabled, uri} = preferences.homeAlgo
|
||||||
|
if (enabled && uri) {
|
||||||
|
result = [HOME_ALGO_FEED_STUB, HOME_FEED_STUB]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await Promise.allSettled([feedsPromise, ...listsPromises])
|
await Promise.allSettled([feedsPromise, ...listsPromises])
|
||||||
for (let pinnedUri of pinnedUris) {
|
for (let pinnedUri of pinnedUris) {
|
||||||
if (resolved.has(pinnedUri)) {
|
if (resolved.has(pinnedUri)) {
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ type AuthorFilter =
|
|||||||
type FeedUri = string
|
type FeedUri = string
|
||||||
type ListUri = string
|
type ListUri = string
|
||||||
export type FeedDescriptor =
|
export type FeedDescriptor =
|
||||||
|
| 'home-algo'
|
||||||
| 'home'
|
| 'home'
|
||||||
| 'following'
|
| 'following'
|
||||||
| `author|${ActorDid}|${AuthorFilter}`
|
| `author|${ActorDid}|${AuthorFilter}`
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import {
|
|
||||||
UsePreferencesQueryResponse,
|
|
||||||
ThreadViewPreferences,
|
|
||||||
} from '#/state/queries/preferences/types'
|
|
||||||
import {DEFAULT_LOGGED_OUT_LABEL_PREFERENCES} from '#/state/queries/preferences/moderation'
|
import {DEFAULT_LOGGED_OUT_LABEL_PREFERENCES} from '#/state/queries/preferences/moderation'
|
||||||
|
import {
|
||||||
|
ThreadViewPreferences,
|
||||||
|
UsePreferencesQueryResponse,
|
||||||
|
} from '#/state/queries/preferences/types'
|
||||||
|
|
||||||
export const DEFAULT_HOME_FEED_PREFS: UsePreferencesQueryResponse['feedViewPrefs'] =
|
export const DEFAULT_HOME_FEED_PREFS: UsePreferencesQueryResponse['feedViewPrefs'] =
|
||||||
{
|
{
|
||||||
@@ -45,4 +45,5 @@ export const DEFAULT_LOGGED_OUT_PREFERENCES: UsePreferencesQueryResponse = {
|
|||||||
threadViewPrefs: DEFAULT_THREAD_VIEW_PREFS,
|
threadViewPrefs: DEFAULT_THREAD_VIEW_PREFS,
|
||||||
userAge: 13, // TODO(pwi)
|
userAge: 13, // TODO(pwi)
|
||||||
interests: {tags: []},
|
interests: {tags: []},
|
||||||
|
homeAlgo: {enabled: undefined},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {RenderTabBarFnProps} from 'view/com/pager/Pager'
|
|
||||||
import {HomeHeaderLayout} from './HomeHeaderLayout'
|
|
||||||
import {FeedSourceInfo} from '#/state/queries/feed'
|
|
||||||
import {useNavigation} from '@react-navigation/native'
|
import {useNavigation} from '@react-navigation/native'
|
||||||
|
|
||||||
|
import {usePalette} from '#/lib/hooks/usePalette'
|
||||||
|
import {FeedSourceInfo} from '#/state/queries/feed'
|
||||||
import {NavigationProp} from 'lib/routes/types'
|
import {NavigationProp} from 'lib/routes/types'
|
||||||
import {isWeb} from 'platform/detection'
|
import {isWeb} from 'platform/detection'
|
||||||
|
import {RenderTabBarFnProps} from 'view/com/pager/Pager'
|
||||||
import {TabBar} from '../pager/TabBar'
|
import {TabBar} from '../pager/TabBar'
|
||||||
import {usePalette} from '#/lib/hooks/usePalette'
|
import {HomeHeaderLayout} from './HomeHeaderLayout'
|
||||||
|
|
||||||
export function HomeHeader(
|
export function HomeHeader(
|
||||||
props: RenderTabBarFnProps & {
|
props: RenderTabBarFnProps & {
|
||||||
@@ -20,7 +21,9 @@ export function HomeHeader(
|
|||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
|
|
||||||
const hasPinnedCustom = React.useMemo<boolean>(() => {
|
const hasPinnedCustom = React.useMemo<boolean>(() => {
|
||||||
return feeds.some(tab => tab.uri !== '')
|
return feeds.some(
|
||||||
|
tab => !['home', 'home-algo', 'following'].includes(tab.uri),
|
||||||
|
)
|
||||||
}, [feeds])
|
}, [feeds])
|
||||||
|
|
||||||
const items = React.useMemo(() => {
|
const items = React.useMemo(() => {
|
||||||
|
|||||||
+32
-13
@@ -53,19 +53,34 @@ function HomeScreenReady({
|
|||||||
pinnedFeedInfos: FeedSourceInfo[]
|
pinnedFeedInfos: FeedSourceInfo[]
|
||||||
}) {
|
}) {
|
||||||
useOTAUpdates()
|
useOTAUpdates()
|
||||||
|
const isHomeAlgoExperimentEnabled = useGate(
|
||||||
|
'reduced_onboarding_and_home_algo',
|
||||||
|
)
|
||||||
|
|
||||||
const allFeeds = React.useMemo(() => {
|
const allFeeds = React.useMemo(() => {
|
||||||
const feeds: FeedDescriptor[] = []
|
const feeds: FeedDescriptor[] = []
|
||||||
feeds.push('home')
|
|
||||||
for (const {uri} of pinnedFeedInfos) {
|
for (const {uri} of pinnedFeedInfos) {
|
||||||
if (uri.includes('app.bsky.feed.generator')) {
|
if (uri.includes('app.bsky.feed.generator')) {
|
||||||
feeds.push(`feedgen|${uri}`)
|
feeds.push(`feedgen|${uri}`)
|
||||||
} else if (uri.includes('app.bsky.graph.list')) {
|
} else if (uri.includes('app.bsky.graph.list')) {
|
||||||
feeds.push(`list|${uri}`)
|
feeds.push(`list|${uri}`)
|
||||||
|
} else if (uri === 'home-algo') {
|
||||||
|
// TODO can I ever end up here without a feed?
|
||||||
|
if (
|
||||||
|
isHomeAlgoExperimentEnabled &&
|
||||||
|
preferences.homeAlgo.enabled &&
|
||||||
|
preferences.homeAlgo.uri
|
||||||
|
) {
|
||||||
|
feeds.push(`feedgen|${preferences.homeAlgo.uri}`)
|
||||||
|
}
|
||||||
|
} else if (uri === 'home') {
|
||||||
|
feeds.push('home')
|
||||||
|
} else if (uri === 'following') {
|
||||||
|
feeds.push('following')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return feeds
|
return feeds
|
||||||
}, [pinnedFeedInfos])
|
}, [pinnedFeedInfos, isHomeAlgoExperimentEnabled, preferences.homeAlgo])
|
||||||
|
|
||||||
const rawSelectedFeed = useSelectedFeed()
|
const rawSelectedFeed = useSelectedFeed()
|
||||||
const setSelectedFeed = useSetSelectedFeed()
|
const setSelectedFeed = useSetSelectedFeed()
|
||||||
@@ -186,7 +201,6 @@ function HomeScreenReady({
|
|||||||
return <CustomFeedEmptyState />
|
return <CustomFeedEmptyState />
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const [homeFeed, ...customFeeds] = allFeeds
|
|
||||||
const homeFeedParams = React.useMemo<FeedParams>(() => {
|
const homeFeedParams = React.useMemo<FeedParams>(() => {
|
||||||
return {
|
return {
|
||||||
mergeFeedEnabled: Boolean(preferences.feedViewPrefs.lab_mergeFeedEnabled),
|
mergeFeedEnabled: Boolean(preferences.feedViewPrefs.lab_mergeFeedEnabled),
|
||||||
@@ -206,16 +220,21 @@ function HomeScreenReady({
|
|||||||
onPageSelected={onPageSelected}
|
onPageSelected={onPageSelected}
|
||||||
onPageScrollStateChanged={onPageScrollStateChanged}
|
onPageScrollStateChanged={onPageScrollStateChanged}
|
||||||
renderTabBar={renderTabBar}>
|
renderTabBar={renderTabBar}>
|
||||||
<FeedPage
|
{allFeeds.map(feed => {
|
||||||
key={homeFeed}
|
if (feed === 'home') {
|
||||||
testID="followingFeedPage"
|
return (
|
||||||
isPageFocused={selectedFeed === homeFeed}
|
<FeedPage
|
||||||
feed={homeFeed}
|
key={'home'}
|
||||||
feedParams={homeFeedParams}
|
testID="followingFeedPage"
|
||||||
renderEmptyState={renderFollowingEmptyState}
|
isPageFocused={selectedFeed === 'home'}
|
||||||
renderEndOfFeed={FollowingEndOfFeed}
|
feed={'home'}
|
||||||
/>
|
feedParams={homeFeedParams}
|
||||||
{customFeeds.map(feed => {
|
renderEmptyState={renderFollowingEmptyState}
|
||||||
|
renderEndOfFeed={FollowingEndOfFeed}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FeedPage
|
<FeedPage
|
||||||
key={feed}
|
key={feed}
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {View, StyleSheet} from 'react-native'
|
import {StyleSheet, View} from 'react-native'
|
||||||
import {useNavigationState, useNavigation} from '@react-navigation/native'
|
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
|
||||||
import {TextLink} from 'view/com/util/Link'
|
|
||||||
import {getCurrentRoute} from 'lib/routes/helpers'
|
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {msg} from '@lingui/macro'
|
import {msg} from '@lingui/macro'
|
||||||
import {usePinnedFeedsInfos} from '#/state/queries/feed'
|
import {useLingui} from '@lingui/react'
|
||||||
import {useSelectedFeed, useSetSelectedFeed} from '#/state/shell/selected-feed'
|
import {useNavigation, useNavigationState} from '@react-navigation/native'
|
||||||
import {FeedDescriptor} from '#/state/queries/post-feed'
|
|
||||||
import {NavigationProp} from 'lib/routes/types'
|
|
||||||
import {emitSoftReset} from '#/state/events'
|
import {emitSoftReset} from '#/state/events'
|
||||||
|
import {usePinnedFeedsInfos} from '#/state/queries/feed'
|
||||||
|
import {FeedDescriptor} from '#/state/queries/post-feed'
|
||||||
|
import {useSelectedFeed, useSetSelectedFeed} from '#/state/shell/selected-feed'
|
||||||
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
|
import {getCurrentRoute} from 'lib/routes/helpers'
|
||||||
|
import {NavigationProp} from 'lib/routes/types'
|
||||||
|
import {TextLink} from 'view/com/util/Link'
|
||||||
|
|
||||||
export function DesktopFeeds() {
|
export function DesktopFeeds() {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
@@ -33,8 +34,10 @@ export function DesktopFeeds() {
|
|||||||
{pinnedFeedInfos.map(feedInfo => {
|
{pinnedFeedInfos.map(feedInfo => {
|
||||||
const uri = feedInfo.uri
|
const uri = feedInfo.uri
|
||||||
let feed: FeedDescriptor
|
let feed: FeedDescriptor
|
||||||
if (!uri) {
|
if (uri === 'home') {
|
||||||
feed = 'home'
|
feed = 'home'
|
||||||
|
} else if (uri === 'home-algo') {
|
||||||
|
feed = 'home-algo'
|
||||||
} else if (uri.includes('app.bsky.feed.generator')) {
|
} else if (uri.includes('app.bsky.feed.generator')) {
|
||||||
feed = `feedgen|${uri}`
|
feed = `feedgen|${uri}`
|
||||||
} else if (uri.includes('app.bsky.graph.list')) {
|
} else if (uri.includes('app.bsky.graph.list')) {
|
||||||
|
|||||||
Reference in New Issue
Block a user