Merge branch 'main' into web-layout
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
+1871
-367
File diff suppressed because it is too large
Load Diff
+1858
-370
File diff suppressed because it is too large
Load Diff
+1871
-367
File diff suppressed because it is too large
Load Diff
+1871
-367
File diff suppressed because it is too large
Load Diff
+1866
-378
File diff suppressed because it is too large
Load Diff
+2049
-437
File diff suppressed because it is too large
Load Diff
+1871
-367
File diff suppressed because it is too large
Load Diff
+1587
-560
File diff suppressed because it is too large
Load Diff
+1867
-379
File diff suppressed because it is too large
Load Diff
+1867
-380
File diff suppressed because it is too large
Load Diff
@@ -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')) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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} />
|
||||
},
|
||||
|
||||
@@ -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])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user