Drop the hard-coded what's hot algo
parent
2f4408582b
commit
84990c509e
|
@ -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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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 => (
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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}
|
||||||
/>
|
/>
|
||||||
|
|
Loading…
Reference in New Issue