Drop the hard-coded what's hot algo

zio/stable
Paul Frazee 2023-05-18 15:12:18 -05:00
parent 2f4408582b
commit 84990c509e
6 changed files with 36 additions and 100 deletions

View File

@ -310,7 +310,7 @@ export class PostsFeedModel {
constructor( constructor(
public rootStore: RootStoreModel, public rootStore: RootStoreModel,
public feedType: 'home' | 'author' | 'suggested' | 'goodstuff' | 'custom', public feedType: 'home' | 'author' | 'suggested' | 'custom',
params: params:
| GetTimeline.QueryParams | GetTimeline.QueryParams
| GetAuthorFeed.QueryParams | GetAuthorFeed.QueryParams
@ -391,10 +391,9 @@ export class PostsFeedModel {
} }
get feedTuners() { get feedTuners() {
if (this.feedType === 'goodstuff') { if (this.feedType === 'custom') {
return [ return [
FeedTuner.dedupReposts, FeedTuner.dedupReposts,
FeedTuner.likedRepliesOnly,
FeedTuner.preferredLangOnly( FeedTuner.preferredLangOnly(
this.rootStore.preferences.contentLanguages, this.rootStore.preferences.contentLanguages,
), ),
@ -701,15 +700,6 @@ export class PostsFeedModel {
return this.rootStore.agent.app.bsky.feed.getFeed( return this.rootStore.agent.app.bsky.feed.getFeed(
params as GetCustomFeed.QueryParams, params as GetCustomFeed.QueryParams,
) )
} else if (this.feedType === 'goodstuff') {
const res = await getGoodStuff(
this.rootStore.session.currentSession?.accessJwt || '',
params as GetTimeline.QueryParams,
)
res.data.feed = (res.data.feed || []).filter(
item => !item.post.author.viewer?.muted,
)
return res
} else { } else {
return this.rootStore.agent.getAuthorFeed( return this.rootStore.agent.getAuthorFeed(
params as GetAuthorFeed.QueryParams, params as GetAuthorFeed.QueryParams,
@ -717,45 +707,3 @@ export class PostsFeedModel {
} }
} }
} }
// HACK
// temporary off-spec route to get the good stuff
// -prf
async function getGoodStuff(
accessJwt: string,
params: GetTimeline.QueryParams,
): Promise<GetTimeline.Response> {
const controller = new AbortController()
const to = setTimeout(() => controller.abort(), 15e3)
const uri = new URL('https://bsky.social/xrpc/app.bsky.unspecced.getPopular')
let k: keyof GetTimeline.QueryParams
for (k in params) {
if (typeof params[k] !== 'undefined') {
uri.searchParams.set(k, String(params[k]))
}
}
const res = await fetch(String(uri), {
method: 'get',
headers: {
accept: 'application/json',
authorization: `Bearer ${accessJwt}`,
},
signal: controller.signal,
})
const resHeaders: Record<string, string> = {}
res.headers.forEach((value: string, key: string) => {
resHeaders[key] = value
})
let resBody = await res.json()
clearTimeout(to)
return {
success: res.status === 200,
headers: resHeaders,
data: jsonToLex(resBody),
}
}

View File

@ -41,8 +41,8 @@ export function Component({}: {}) {
<View testID="contentLanguagesModal" style={[pal.view, styles.container]}> <View testID="contentLanguagesModal" style={[pal.view, styles.container]}>
<Text style={[pal.text, styles.title]}>Content Languages</Text> <Text style={[pal.text, styles.title]}>Content Languages</Text>
<Text style={[pal.text, styles.description]}> <Text style={[pal.text, styles.description]}>
Which languages would you like to see in the What's Hot feed? (Leave Which languages would you like to see in the your feed? (Leave them all
them all unchecked to see any language.) unchecked to see any language.)
</Text> </Text>
<ScrollView style={styles.scrollContainer}> <ScrollView style={styles.scrollContainer}>
{languages.map(lang => ( {languages.map(lang => (

View File

@ -28,12 +28,7 @@ const FeedsTabBarDesktop = observer(
) => { ) => {
const store = useStores() const store = useStores()
const items = useMemo( const items = useMemo(
() => [ () => ['Following', ...store.me.savedFeeds.pinnedFeedNames, 'My feeds'],
'Following',
"What's hot",
...store.me.savedFeeds.pinnedFeedNames,
'My feeds',
],
[store.me.savedFeeds.pinnedFeedNames], [store.me.savedFeeds.pinnedFeedNames],
) )
const pal = usePalette('default') const pal = usePalette('default')

View File

@ -33,12 +33,7 @@ export const FeedsTabBar = observer(
}, [store]) }, [store])
const items = useMemo( const items = useMemo(
() => [ () => ['Following', ...store.me.savedFeeds.pinnedFeedNames, 'My feeds'],
'Following',
"What's hot",
...store.me.savedFeeds.pinnedFeedNames,
'My feeds',
],
[store.me.savedFeeds.pinnedFeedNames], [store.me.savedFeeds.pinnedFeedNames],
) )

View File

@ -1,5 +1,6 @@
import React from 'react' import React from 'react'
import {StyleSheet, View} from 'react-native' import {StyleSheet, View} from 'react-native'
import {useNavigation} from '@react-navigation/native'
import { import {
FontAwesomeIcon, FontAwesomeIcon,
FontAwesomeIconStyle, FontAwesomeIconStyle,
@ -7,14 +8,21 @@ import {
import {Text} from '../util/text/Text' import {Text} from '../util/text/Text'
import {Button} from '../util/forms/Button' import {Button} from '../util/forms/Button'
import {MagnifyingGlassIcon} from 'lib/icons' import {MagnifyingGlassIcon} from 'lib/icons'
import {NavigationProp} from 'lib/routes/types'
import {useStores} from 'state/index' import {useStores} from 'state/index'
import {usePalette} from 'lib/hooks/usePalette' import {usePalette} from 'lib/hooks/usePalette'
import {s} from 'lib/styles' import {s} from 'lib/styles'
export function WhatsHotEmptyState() { export function CustomFeedEmptyState() {
const pal = usePalette('default') const pal = usePalette('default')
const palInverted = usePalette('inverted') const palInverted = usePalette('inverted')
const store = useStores() const store = useStores()
const navigation = useNavigation<NavigationProp>()
const onPressFindAccounts = React.useCallback(() => {
navigation.navigate('SearchTab')
navigation.popToTop()
}, [navigation])
const onPressSettings = React.useCallback(() => { const onPressSettings = React.useCallback(() => {
store.shell.openModal({name: 'content-languages-settings'}) store.shell.openModal({name: 'content-languages-settings'})
@ -26,9 +34,22 @@ export function WhatsHotEmptyState() {
<MagnifyingGlassIcon style={[styles.emptyIcon, pal.text]} size={62} /> <MagnifyingGlassIcon style={[styles.emptyIcon, pal.text]} size={62} />
</View> </View>
<Text type="xl-medium" style={[s.textCenter, pal.text]}> <Text type="xl-medium" style={[s.textCenter, pal.text]}>
Your What's Hot feed is empty! This is because there aren't enough users This feed is empty! You may need to follow more users or tune your
posting in your selected language. language settings.
</Text> </Text>
<Button
type="inverted"
style={styles.emptyBtn}
onPress={onPressFindAccounts}>
<Text type="lg-medium" style={palInverted.text}>
Find accounts to follow
</Text>
<FontAwesomeIcon
icon="angle-right"
style={palInverted.text as FontAwesomeIconStyle}
size={14}
/>
</Button>
<Button type="inverted" style={styles.emptyBtn} onPress={onPressSettings}> <Button type="inverted" style={styles.emptyBtn} onPress={onPressSettings}>
<Text type="lg-medium" style={palInverted.text}> <Text type="lg-medium" style={palInverted.text}>
Update my settings Update my settings

View File

@ -11,7 +11,7 @@ import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {useTabFocusEffect} from 'lib/hooks/useTabFocusEffect' import {useTabFocusEffect} from 'lib/hooks/useTabFocusEffect'
import {Feed} from '../com/posts/Feed' import {Feed} from '../com/posts/Feed'
import {FollowingEmptyState} from 'view/com/posts/FollowingEmptyState' import {FollowingEmptyState} from 'view/com/posts/FollowingEmptyState'
import {WhatsHotEmptyState} from 'view/com/posts/WhatsHotEmptyState' import {CustomFeedEmptyState} from 'view/com/posts/CustomFeedEmptyState'
import {LoadLatestBtn} from '../com/util/load-latest/LoadLatestBtn' import {LoadLatestBtn} from '../com/util/load-latest/LoadLatestBtn'
import {FeedsTabBar} from '../com/pager/FeedsTabBar' import {FeedsTabBar} from '../com/pager/FeedsTabBar'
import {Pager, PagerRef, RenderTabBarFnProps} from 'view/com/pager/Pager' import {Pager, PagerRef, RenderTabBarFnProps} from 'view/com/pager/Pager'
@ -34,15 +34,6 @@ export const HomeScreen = withAuthRequired(
const pagerRef = React.useRef<PagerRef>(null) const pagerRef = React.useRef<PagerRef>(null)
const [selectedPage, setSelectedPage] = React.useState(0) const [selectedPage, setSelectedPage] = React.useState(0)
const [customFeeds, setCustomFeeds] = React.useState<PostsFeedModel[]>([]) const [customFeeds, setCustomFeeds] = React.useState<PostsFeedModel[]>([])
const [initialLanguages] = React.useState(
store.preferences.contentLanguages,
)
const algoFeed: PostsFeedModel = React.useMemo(() => {
const feed = new PostsFeedModel(store, 'goodstuff', {})
feed.setup()
return feed
}, [store])
React.useEffect(() => { React.useEffect(() => {
const {pinned} = store.me.savedFeeds const {pinned} = store.me.savedFeeds
@ -66,13 +57,6 @@ export const HomeScreen = withAuthRequired(
setCustomFeeds(feeds) setCustomFeeds(feeds)
}, [store, store.me.savedFeeds.pinned, customFeeds, setCustomFeeds]) }, [store, store.me.savedFeeds.pinned, customFeeds, setCustomFeeds])
React.useEffect(() => {
// refresh whats hot when lang preferences change
if (initialLanguages !== store.preferences.contentLanguages) {
algoFeed.refresh()
}
}, [initialLanguages, store.preferences.contentLanguages, algoFeed])
useFocusEffect( useFocusEffect(
React.useCallback(() => { React.useCallback(() => {
store.shell.setMinimalShellMode(false) store.shell.setMinimalShellMode(false)
@ -113,8 +97,8 @@ export const HomeScreen = withAuthRequired(
return <FollowingEmptyState /> return <FollowingEmptyState />
}, []) }, [])
const renderWhatsHotEmptyState = React.useCallback(() => { const renderCustomFeedEmptyState = React.useCallback(() => {
return <WhatsHotEmptyState /> return <CustomFeedEmptyState />
}, []) }, [])
const initialPage = store.me.followsCount === 0 ? 1 : 0 const initialPage = store.me.followsCount === 0 ? 1 : 0
@ -133,26 +117,19 @@ export const HomeScreen = withAuthRequired(
feed={store.me.mainFeed} feed={store.me.mainFeed}
renderEmptyState={renderFollowingEmptyState} renderEmptyState={renderFollowingEmptyState}
/> />
<FeedPage
key="2"
testID="whatshotFeedPage"
isPageFocused={selectedPage === 1}
feed={algoFeed}
renderEmptyState={renderWhatsHotEmptyState}
/>
{customFeeds.map((f, index) => { {customFeeds.map((f, index) => {
return ( return (
<FeedPage <FeedPage
key={(f.params as GetCustomFeed.QueryParams).feed} key={(f.params as GetCustomFeed.QueryParams).feed}
testID="customFeedPage" testID="customFeedPage"
isPageFocused={selectedPage === 2 + index} isPageFocused={selectedPage === 1 + index}
feed={f} feed={f}
renderEmptyState={renderFollowingEmptyState} renderEmptyState={renderCustomFeedEmptyState}
/> />
) )
})} })}
<SavedFeeds <SavedFeeds
key={String(3 + store.me.savedFeeds.pinned.length)} key={String(2 + store.me.savedFeeds.pinned.length)}
headerOffset={HEADER_OFFSET} headerOffset={HEADER_OFFSET}
isPageFocused={selectedPage === 2 + store.me.savedFeeds.pinned.length} isPageFocused={selectedPage === 2 + store.me.savedFeeds.pinned.length}
/> />