Merge branch 'main' into web-layout

This commit is contained in:
Dan Abramov
2024-01-16 21:46:25 +00:00
29 changed files with 19127 additions and 4313 deletions
+18 -9
View File
@@ -4,15 +4,20 @@ import {
} from '@atproto/api'
import {FeedAPI, FeedAPIResponse} from './types'
import {getAgent} from '#/state/session'
import {getContentLanguages} from '#/state/preferences/languages'
export class CustomFeedAPI implements FeedAPI {
constructor(public params: GetCustomFeed.QueryParams) {}
async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> {
const res = await getAgent().app.bsky.feed.getFeed({
...this.params,
limit: 1,
})
const contentLangs = getContentLanguages().join(',')
const res = await getAgent().app.bsky.feed.getFeed(
{
...this.params,
limit: 1,
},
{headers: {'Accept-Language': contentLangs}},
)
return res.data.feed[0]
}
@@ -23,11 +28,15 @@ export class CustomFeedAPI implements FeedAPI {
cursor: string | undefined
limit: number
}): Promise<FeedAPIResponse> {
const res = await getAgent().app.bsky.feed.getFeed({
...this.params,
cursor,
limit,
})
const contentLangs = getContentLanguages().join(',')
const res = await getAgent().app.bsky.feed.getFeed(
{
...this.params,
cursor,
limit,
},
{headers: {'Accept-Language': contentLangs}},
)
if (res.success) {
// NOTE
// some custom feeds fail to enforce the pagination limit
+89
View File
@@ -0,0 +1,89 @@
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 (!returnCursor) {
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,
}
}
}
+11 -6
View File
@@ -8,6 +8,7 @@ import {FeedAPI, FeedAPIResponse, ReasonFeedSource} from './types'
import {FeedParams} from '#/state/queries/post-feed'
import {FeedTunerFn} from '../feed-manip'
import {getAgent} from '#/state/session'
import {getContentLanguages} from '#/state/preferences/languages'
const REQUEST_WAIT_MS = 500 // 500ms
const POST_AGE_CUTOFF = 60e3 * 60 * 24 // 24hours
@@ -25,7 +26,7 @@ export class MergeFeedAPI implements FeedAPI {
reset() {
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.itemCursor = 0
this.sampleCursor = 0
@@ -231,11 +232,15 @@ class MergeFeedSource_Custom extends MergeFeedSource {
limit: number,
): Promise<AppBskyFeedGetTimeline.Response> {
try {
const res = await getAgent().app.bsky.feed.getFeed({
cursor,
limit,
feed: this.feedUri,
})
const contentLangs = getContentLanguages().join(',')
const res = await getAgent().app.bsky.feed.getFeed(
{
cursor,
limit,
feed: this.feedUri,
},
{headers: {'Accept-Language': contentLangs}},
)
// NOTE
// some custom feeds fail to enforce the pagination limit
// so we manually truncate here
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+6 -1
View File
@@ -18,6 +18,7 @@ import {LikesFeedAPI} from 'lib/api/feed/likes'
import {CustomFeedAPI} from 'lib/api/feed/custom'
import {ListFeedAPI} from 'lib/api/feed/list'
import {MergeFeedAPI} from 'lib/api/feed/merge'
import {HomeFeedAPI} from '#/lib/api/feed/home'
import {logger} from '#/logger'
import {STALE} from '#/state/queries'
import {precacheFeedPosts as precacheResolvedUris} from './resolve-uri'
@@ -338,7 +339,11 @@ function createApi(
feedTuners: FeedTunerFn[],
) {
if (feedDesc === 'home') {
return new MergeFeedAPI(params, feedTuners)
if (params.mergeFeedEnabled) {
return new MergeFeedAPI(params, feedTuners)
} else {
return new HomeFeedAPI()
}
} else if (feedDesc === 'following') {
return new FollowingFeedAPI()
} else if (feedDesc.startsWith('author')) {
+6
View File
@@ -223,6 +223,12 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
throw new Error(`session: createAccount failed to establish a session`)
}
/*dont await*/ agent.upsertProfile(_existing => {
return {
displayName: handle,
}
})
const account: SessionAccount = {
service: agent.service.toString(),
did: agent.session.did,
+13 -5
View File
@@ -94,15 +94,23 @@ export const ListCard = ({
</Trans>
))}
</Text>
{!!list.viewer?.muted && (
<View style={s.flexRow}>
<View style={s.flexRow}>
{list.viewer?.muted ? (
<View style={[s.mt5, pal.btn, styles.pill]}>
<Text type="xs" style={pal.text}>
<Trans>Subscribed</Trans>
<Trans>Muted</Trans>
</Text>
</View>
</View>
)}
) : null}
{list.viewer?.blocked ? (
<View style={[s.mt5, pal.btn, styles.pill]}>
<Text type="xs" style={pal.text}>
<Trans>Blocked</Trans>
</Text>
</View>
) : null}
</View>
</View>
{renderButton ? (
<View style={styles.layoutButton}>{renderButton()}</View>
+5 -2
View File
@@ -160,7 +160,7 @@ export function Component({}: {}) {
{/* TODO: Update this label to be more concise */}
<Text
type="lg"
style={styles.description}
style={[pal.text, styles.description]}
nativeID="confirmationCode">
<Trans>
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`,
)}
/>
<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>
</Text>
<TextInput
@@ -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>
)
}
+8
View File
@@ -30,6 +30,8 @@ import {useSession} from '#/state/session'
import {STALE} from '#/state/queries'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {DiscoverFallbackHeader} from './DiscoverFallbackHeader'
import {FALLBACK_MARKER_POST} from '#/lib/api/feed/home'
const LOADING_ITEM = {_reactKey: '__loading__'}
const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
@@ -265,6 +267,12 @@ let Feed = ({
)
} else if (item === LOADING_ITEM) {
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} />
},
+1 -2
View File
@@ -19,7 +19,6 @@ import {useSession} from '#/state/session'
import {loadString, saveString} from '#/lib/storage'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {clamp} from '#/lib/numbers'
import {PROD_DEFAULT_FEED} from '#/lib/constants'
type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'>
export function HomeScreen(props: Props) {
@@ -112,7 +111,7 @@ function HomeScreenReady({
mergeFeedEnabled: Boolean(preferences.feedViewPrefs.lab_mergeFeedEnabled),
mergeFeedSources: preferences.feedViewPrefs.lab_mergeFeedEnabled
? preferences.feeds.saved
: [PROD_DEFAULT_FEED('whats-hot')],
: [],
}
}, [preferences])