Add a new home feed-api wrapper and give a header indicating the fallback behavior (#2534)

* Add a new home feed-api wrapper and give a header indicating the fallback behavior

* Sneak in a quick fix: use the correct text color in the delete modal

* Use imported constant
zio/stable
Paul Frazee 2024-01-15 15:03:54 -08:00 committed by GitHub
parent 7df0b7ade1
commit a7d617c7a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 152 additions and 6 deletions

View File

@ -0,0 +1,88 @@
import {AppBskyFeedDefs} from '@atproto/api'
import {FeedAPI, FeedAPIResponse} from './types'
import {FollowingFeedAPI} from './following'
import {CustomFeedAPI} from './custom'
import {PROD_DEFAULT_FEED} from '#/lib/constants'
// HACK
// the feed API does not include any facilities for passing down
// non-post elements. adding that is a bit of a heavy lift, and we
// have just one temporary usecase for it: flagging when the home feed
// falls back to discover.
// we use this fallback marker post to drive this instead. see Feed.tsx
// for the usage.
// -prf
export const FALLBACK_MARKER_POST: AppBskyFeedDefs.FeedViewPost = {
post: {
uri: 'fallback-marker-post',
cid: 'fake',
record: {},
author: {
did: 'did:fake',
handle: 'fake.com',
},
indexedAt: new Date().toISOString(),
},
}
export class HomeFeedAPI implements FeedAPI {
following: FollowingFeedAPI
discover: CustomFeedAPI
usingDiscover = false
itemCursor = 0
constructor() {
this.following = new FollowingFeedAPI()
this.discover = new CustomFeedAPI({feed: PROD_DEFAULT_FEED('whats-hot')})
}
reset() {
this.following = new FollowingFeedAPI()
this.discover = new CustomFeedAPI({feed: PROD_DEFAULT_FEED('whats-hot')})
this.usingDiscover = false
this.itemCursor = 0
}
async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> {
if (this.usingDiscover) {
return this.discover.peekLatest()
}
return this.following.peekLatest()
}
async fetch({
cursor,
limit,
}: {
cursor: string | undefined
limit: number
}): Promise<FeedAPIResponse> {
if (!cursor) {
this.reset()
}
let returnCursor
let posts: AppBskyFeedDefs.FeedViewPost[] = []
if (!this.usingDiscover) {
const res = await this.following.fetch({cursor, limit})
returnCursor = res.cursor
posts = posts.concat(res.feed)
if (res.feed.length === 0 || !cursor) {
posts.push(FALLBACK_MARKER_POST)
this.usingDiscover = true
}
}
if (this.usingDiscover) {
const res = await this.discover.fetch({cursor, limit})
returnCursor = res.cursor
posts = posts.concat(res.feed)
}
return {
cursor: returnCursor,
feed: posts,
}
}
}

View File

@ -26,7 +26,7 @@ export class MergeFeedAPI implements FeedAPI {
reset() { reset() {
this.following = new MergeFeedSource_Following(this.feedTuners) this.following = new MergeFeedSource_Following(this.feedTuners)
this.customFeeds = [] // just empty the array, they will be captured in _fetchNext() this.customFeeds = []
this.feedCursor = 0 this.feedCursor = 0
this.itemCursor = 0 this.itemCursor = 0
this.sampleCursor = 0 this.sampleCursor = 0

View File

@ -18,6 +18,7 @@ import {LikesFeedAPI} from 'lib/api/feed/likes'
import {CustomFeedAPI} from 'lib/api/feed/custom' import {CustomFeedAPI} from 'lib/api/feed/custom'
import {ListFeedAPI} from 'lib/api/feed/list' import {ListFeedAPI} from 'lib/api/feed/list'
import {MergeFeedAPI} from 'lib/api/feed/merge' import {MergeFeedAPI} from 'lib/api/feed/merge'
import {HomeFeedAPI} from '#/lib/api/feed/home'
import {logger} from '#/logger' import {logger} from '#/logger'
import {STALE} from '#/state/queries' import {STALE} from '#/state/queries'
import {precacheFeedPosts as precacheResolvedUris} from './resolve-uri' import {precacheFeedPosts as precacheResolvedUris} from './resolve-uri'
@ -338,7 +339,11 @@ function createApi(
feedTuners: FeedTunerFn[], feedTuners: FeedTunerFn[],
) { ) {
if (feedDesc === 'home') { if (feedDesc === 'home') {
return new MergeFeedAPI(params, feedTuners) if (params.mergeFeedEnabled) {
return new MergeFeedAPI(params, feedTuners)
} else {
return new HomeFeedAPI()
}
} else if (feedDesc === 'following') { } else if (feedDesc === 'following') {
return new FollowingFeedAPI() return new FollowingFeedAPI()
} else if (feedDesc.startsWith('author')) { } else if (feedDesc.startsWith('author')) {

View File

@ -160,7 +160,7 @@ export function Component({}: {}) {
{/* TODO: Update this label to be more concise */} {/* TODO: Update this label to be more concise */}
<Text <Text
type="lg" type="lg"
style={styles.description} style={[pal.text, styles.description]}
nativeID="confirmationCode"> nativeID="confirmationCode">
<Trans> <Trans>
Check your inbox for an email with the confirmation code to Check your inbox for an email with the confirmation code to
@ -180,7 +180,10 @@ export function Component({}: {}) {
msg`Input confirmation code for account deletion`, msg`Input confirmation code for account deletion`,
)} )}
/> />
<Text type="lg" style={styles.description} nativeID="password"> <Text
type="lg"
style={[pal.text, styles.description]}
nativeID="password">
<Trans>Please enter your password as well:</Trans> <Trans>Please enter your password as well:</Trans>
</Text> </Text>
<TextInput <TextInput

View File

@ -0,0 +1,43 @@
import React from 'react'
import {View} from 'react-native'
import {Trans} from '@lingui/macro'
import {Text} from '../util/text/Text'
import {usePalette} from '#/lib/hooks/usePalette'
import {TextLink} from '../util/Link'
import {InfoCircleIcon} from '#/lib/icons'
export function DiscoverFallbackHeader() {
const pal = usePalette('default')
return (
<View
style={[
{
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 12,
paddingHorizontal: 12,
borderTopWidth: 1,
},
pal.border,
pal.viewLight,
]}>
<View style={{width: 68, paddingLeft: 12}}>
<InfoCircleIcon size={36} style={pal.textLight} strokeWidth={1.5} />
</View>
<View style={{flex: 1}}>
<Text type="md" style={pal.text}>
<Trans>
We ran out of posts from your follows. Here's the latest from
</Trans>{' '}
<TextLink
type="md-medium"
href="/profile/bsky.app/feed/whats-hot"
text="Discover"
style={pal.link}
/>
.
</Text>
</View>
</View>
)
}

View File

@ -30,6 +30,8 @@ import {useSession} from '#/state/session'
import {STALE} from '#/state/queries' import {STALE} from '#/state/queries'
import {msg} from '@lingui/macro' import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {DiscoverFallbackHeader} from './DiscoverFallbackHeader'
import {FALLBACK_MARKER_POST} from '#/lib/api/feed/home'
const LOADING_ITEM = {_reactKey: '__loading__'} const LOADING_ITEM = {_reactKey: '__loading__'}
const EMPTY_FEED_ITEM = {_reactKey: '__empty__'} const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
@ -265,6 +267,12 @@ let Feed = ({
) )
} else if (item === LOADING_ITEM) { } else if (item === LOADING_ITEM) {
return <PostFeedLoadingPlaceholder /> return <PostFeedLoadingPlaceholder />
} else if (item.rootUri === FALLBACK_MARKER_POST.post.uri) {
// HACK
// tell the user we fell back to discover
// see home.ts (feed api) for more info
// -prf
return <DiscoverFallbackHeader />
} }
return <FeedSlice slice={item} /> return <FeedSlice slice={item} />
}, },

View File

@ -19,7 +19,6 @@ import {useSession} from '#/state/session'
import {loadString, saveString} from '#/lib/storage' import {loadString, saveString} from '#/lib/storage'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {clamp} from '#/lib/numbers' import {clamp} from '#/lib/numbers'
import {PROD_DEFAULT_FEED} from '#/lib/constants'
type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'> type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'>
export function HomeScreen(props: Props) { export function HomeScreen(props: Props) {
@ -112,7 +111,7 @@ function HomeScreenReady({
mergeFeedEnabled: Boolean(preferences.feedViewPrefs.lab_mergeFeedEnabled), mergeFeedEnabled: Boolean(preferences.feedViewPrefs.lab_mergeFeedEnabled),
mergeFeedSources: preferences.feedViewPrefs.lab_mergeFeedEnabled mergeFeedSources: preferences.feedViewPrefs.lab_mergeFeedEnabled
? preferences.feeds.saved ? preferences.feeds.saved
: [PROD_DEFAULT_FEED('whats-hot')], : [],
} }
}, [preferences]) }, [preferences])