Preserve thread numbering in placeholders (#11616)
This commit is contained in:
@@ -0,0 +1,54 @@
|
|||||||
|
import {type app} from '#/lexicons'
|
||||||
|
import {createFeedViewPostsSlices} from './feed-manip'
|
||||||
|
|
||||||
|
jest.mock('./feed/home', () => ({
|
||||||
|
FALLBACK_MARKER_POST: {post: {uri: 'at://did:plc:test/app.bsky.feed.post/1'}},
|
||||||
|
}))
|
||||||
|
|
||||||
|
const author = {
|
||||||
|
$type: 'app.bsky.actor.defs#profileViewBasic',
|
||||||
|
did: 'did:plc:alice',
|
||||||
|
handle: 'alice.test',
|
||||||
|
} as app.bsky.actor.defs.ProfileViewBasic
|
||||||
|
|
||||||
|
function post(id: string) {
|
||||||
|
return {
|
||||||
|
$type: 'app.bsky.feed.defs#postView',
|
||||||
|
uri: `at://did:plc:alice/app.bsky.feed.post/${id}`,
|
||||||
|
cid: id,
|
||||||
|
author,
|
||||||
|
record: {
|
||||||
|
$type: 'app.bsky.feed.post',
|
||||||
|
text: id,
|
||||||
|
createdAt: '2026-08-31T00:00:00.000Z',
|
||||||
|
},
|
||||||
|
indexedAt: '2026-08-31T00:00:00.000Z',
|
||||||
|
} as app.bsky.feed.defs.PostView
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('createFeedViewPostsSlices', () => {
|
||||||
|
it('preserves selected numbering and infers hydrated parent and root numbering', () => {
|
||||||
|
const root = post('root')
|
||||||
|
const parent = post('parent')
|
||||||
|
const selected = post('selected')
|
||||||
|
const feedPost = {
|
||||||
|
post: selected,
|
||||||
|
reply: {root, parent},
|
||||||
|
opThreadPostIndex: 3,
|
||||||
|
opThreadPostCount: 4,
|
||||||
|
} as app.bsky.feed.defs.FeedViewPost & {
|
||||||
|
opThreadPostIndex: number
|
||||||
|
opThreadPostCount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const [slice] = createFeedViewPostsSlices([feedPost])
|
||||||
|
|
||||||
|
expect(
|
||||||
|
slice.items.map(item => [item.post.uri, item.postNumbering]),
|
||||||
|
).toEqual([
|
||||||
|
[root.uri, {opThreadPostIndex: 1, opThreadPostCount: 4}],
|
||||||
|
[parent.uri, {opThreadPostIndex: 2, opThreadPostCount: 4}],
|
||||||
|
[selected.uri, {opThreadPostIndex: 3, opThreadPostCount: 4}],
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
||||||
+20
-14
@@ -9,7 +9,7 @@ export type FeedPostNumbering = Pick<
|
|||||||
'opThreadPostIndex' | 'opThreadPostCount'
|
'opThreadPostIndex' | 'opThreadPostCount'
|
||||||
>
|
>
|
||||||
|
|
||||||
type ValidFeedPostNumbering = Required<FeedPostNumbering>
|
export type ValidFeedPostNumbering = Required<FeedPostNumbering>
|
||||||
|
|
||||||
// AppView adds these fields to feed responses ahead of their feed lexicon.
|
// AppView adds these fields to feed responses ahead of their feed lexicon.
|
||||||
type FeedViewPost = app.bsky.feed.defs.FeedViewPost & FeedPostNumbering
|
type FeedViewPost = app.bsky.feed.defs.FeedViewPost & FeedPostNumbering
|
||||||
@@ -62,7 +62,7 @@ export type FeedTunerFn = (
|
|||||||
type FeedSliceItem = {
|
type FeedSliceItem = {
|
||||||
post: app.bsky.feed.defs.PostView
|
post: app.bsky.feed.defs.PostView
|
||||||
record: app.bsky.feed.post.Main
|
record: app.bsky.feed.post.Main
|
||||||
postNumbering: FeedPostNumbering | undefined
|
postNumbering: ValidFeedPostNumbering | undefined
|
||||||
parentAuthor: app.bsky.actor.defs.ProfileViewBasic | undefined
|
parentAuthor: app.bsky.actor.defs.ProfileViewBasic | undefined
|
||||||
isParentBlocked: boolean
|
isParentBlocked: boolean
|
||||||
isParentNotFound: boolean
|
isParentNotFound: boolean
|
||||||
@@ -88,7 +88,7 @@ export class FeedViewPostsSlice {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
feedPost: FeedViewPost,
|
feedPost: FeedViewPost,
|
||||||
postNumberingByUri: Map<string, FeedPostNumbering>,
|
postNumberingByUri: Map<string, ValidFeedPostNumbering>,
|
||||||
) {
|
) {
|
||||||
const {post, reply, reason} = feedPost
|
const {post, reply, reason} = feedPost
|
||||||
this.items = []
|
this.items = []
|
||||||
@@ -286,6 +286,22 @@ export class FeedViewPostsSlice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createFeedViewPostsSlices(
|
||||||
|
feed: FeedViewPost[],
|
||||||
|
): FeedViewPostsSlice[] {
|
||||||
|
const postNumberingByUri = new Map<string, ValidFeedPostNumbering>()
|
||||||
|
for (const item of feed) {
|
||||||
|
const postNumbering = getPostNumbering(item)
|
||||||
|
if (postNumbering) {
|
||||||
|
postNumberingByUri.set(item.post.uri, postNumbering)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return feed
|
||||||
|
.map(item => new FeedViewPostsSlice(item, postNumberingByUri))
|
||||||
|
.filter(slice => slice.items.length > 0 || slice.isFallbackMarker)
|
||||||
|
}
|
||||||
|
|
||||||
export class FeedTuner {
|
export class FeedTuner {
|
||||||
seenKeys: Set<string> = new Set()
|
seenKeys: Set<string> = new Set()
|
||||||
seenUris: Set<string> = new Set()
|
seenUris: Set<string> = new Set()
|
||||||
@@ -299,17 +315,7 @@ export class FeedTuner {
|
|||||||
dryRun: false,
|
dryRun: false,
|
||||||
},
|
},
|
||||||
): FeedViewPostsSlice[] {
|
): FeedViewPostsSlice[] {
|
||||||
const postNumberingByUri = new Map<string, FeedPostNumbering>()
|
let slices = createFeedViewPostsSlices(feed)
|
||||||
for (const item of feed) {
|
|
||||||
const postNumbering = getPostNumbering(item)
|
|
||||||
if (postNumbering) {
|
|
||||||
postNumberingByUri.set(item.post.uri, postNumbering)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let slices: FeedViewPostsSlice[] = feed
|
|
||||||
.map(item => new FeedViewPostsSlice(item, postNumberingByUri))
|
|
||||||
.filter(s => s.items.length > 0 || s.isFallbackMarker)
|
|
||||||
|
|
||||||
// run the custom tuners
|
// run the custom tuners
|
||||||
for (const tunerFn of this.tunerFns) {
|
for (const tunerFn of this.tunerFns) {
|
||||||
|
|||||||
@@ -11,7 +11,11 @@ import {
|
|||||||
|
|
||||||
import {CustomFeedAPI} from '#/lib/api/feed/custom'
|
import {CustomFeedAPI} from '#/lib/api/feed/custom'
|
||||||
import {aggregateUserInterests} from '#/lib/api/feed/utils'
|
import {aggregateUserInterests} from '#/lib/api/feed/utils'
|
||||||
import {FeedTuner} from '#/lib/api/feed-manip'
|
import {
|
||||||
|
createFeedViewPostsSlices,
|
||||||
|
FeedTuner,
|
||||||
|
type ValidFeedPostNumbering,
|
||||||
|
} from '#/lib/api/feed-manip'
|
||||||
import {cleanError} from '#/lib/strings/errors'
|
import {cleanError} from '#/lib/strings/errors'
|
||||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||||
import {
|
import {
|
||||||
@@ -403,6 +407,35 @@ export function* findAllPostsInQueryData(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function findPostNumberingInQueryData(
|
||||||
|
queryClient: QueryClient,
|
||||||
|
uri: string,
|
||||||
|
): ValidFeedPostNumbering | undefined {
|
||||||
|
const atUri = new AtUri(uri)
|
||||||
|
const queryDatas = queryClient.getQueriesData<
|
||||||
|
InfiniteData<{
|
||||||
|
feed: app.bsky.feed.defs.GeneratorView
|
||||||
|
posts: app.bsky.feed.defs.FeedViewPost[]
|
||||||
|
}>
|
||||||
|
>({
|
||||||
|
queryKey: [RQKEY_ROOT],
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const [_queryKey, queryData] of queryDatas) {
|
||||||
|
if (!queryData?.pages) continue
|
||||||
|
|
||||||
|
for (const page of queryData.pages) {
|
||||||
|
for (const slice of createFeedViewPostsSlices(page.posts)) {
|
||||||
|
for (const item of slice.items) {
|
||||||
|
if (item.postNumbering && didOrHandleUriMatches(atUri, item.post)) {
|
||||||
|
return item.postNumbering
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function* findAllProfilesInQueryData(
|
export function* findAllProfilesInQueryData(
|
||||||
queryClient: QueryClient,
|
queryClient: QueryClient,
|
||||||
did: string,
|
did: string,
|
||||||
|
|||||||
@@ -26,9 +26,11 @@ import {PostListFeedAPI} from '#/lib/api/feed/posts'
|
|||||||
import {type FeedAPI, type ReasonFeedSource} from '#/lib/api/feed/types'
|
import {type FeedAPI, type ReasonFeedSource} from '#/lib/api/feed/types'
|
||||||
import {aggregateUserInterests} from '#/lib/api/feed/utils'
|
import {aggregateUserInterests} from '#/lib/api/feed/utils'
|
||||||
import {
|
import {
|
||||||
|
createFeedViewPostsSlices,
|
||||||
type FeedPostNumbering,
|
type FeedPostNumbering,
|
||||||
FeedTuner,
|
FeedTuner,
|
||||||
type FeedTunerFn,
|
type FeedTunerFn,
|
||||||
|
type ValidFeedPostNumbering,
|
||||||
} from '#/lib/api/feed-manip'
|
} from '#/lib/api/feed-manip'
|
||||||
import {DISCOVER_FEED_URI} from '#/lib/constants'
|
import {DISCOVER_FEED_URI} from '#/lib/constants'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
@@ -527,6 +529,32 @@ export function* findAllPostsInQueryData(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function findPostNumberingInQueryData(
|
||||||
|
queryClient: QueryClient,
|
||||||
|
uri: string,
|
||||||
|
): ValidFeedPostNumbering | undefined {
|
||||||
|
const atUri = new AtUri(uri)
|
||||||
|
const queryDatas = queryClient.getQueriesData<
|
||||||
|
InfiniteData<FeedPageUnselected>
|
||||||
|
>({
|
||||||
|
queryKey: [RQKEY_ROOT],
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const [_queryKey, queryData] of queryDatas) {
|
||||||
|
if (!queryData?.pages) continue
|
||||||
|
|
||||||
|
for (const page of queryData.pages) {
|
||||||
|
for (const slice of createFeedViewPostsSlices(page.feed)) {
|
||||||
|
for (const item of slice.items) {
|
||||||
|
if (item.postNumbering && didOrHandleUriMatches(atUri, item.post)) {
|
||||||
|
return item.postNumbering
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function* findAllProfilesInQueryData(
|
export function* findAllProfilesInQueryData(
|
||||||
queryClient: QueryClient,
|
queryClient: QueryClient,
|
||||||
did: string,
|
did: string,
|
||||||
|
|||||||
@@ -0,0 +1,117 @@
|
|||||||
|
import {QueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
|
import {findAllPostsInQueryData as findAllPostsInBookmarksQueryData} from '#/state/queries/bookmarks/useBookmarksQuery'
|
||||||
|
import {
|
||||||
|
findAllPostsInQueryData as findAllPostsInExploreFeedPreviewsQueryData,
|
||||||
|
findPostNumberingInQueryData as findPostNumberingInExploreFeedPreviewsQueryData,
|
||||||
|
} from '#/state/queries/explore-feed-previews'
|
||||||
|
import {findAllPostsInQueryData as findAllPostsInNotifsQueryData} from '#/state/queries/notifications/feed'
|
||||||
|
import {
|
||||||
|
findAllPostsInQueryData as findAllPostsInFeedQueryData,
|
||||||
|
findPostNumberingInQueryData as findPostNumberingInFeedQueryData,
|
||||||
|
} from '#/state/queries/post-feed'
|
||||||
|
import {findAllPostsInQueryData as findAllPostsInQuoteQueryData} from '#/state/queries/post-quotes'
|
||||||
|
import {findAllPostsInQueryData as findAllPostsInSearchQueryData} from '#/state/queries/search-posts-v2'
|
||||||
|
import {type app} from '#/lexicons'
|
||||||
|
import {getThreadPlaceholder} from './queryCache'
|
||||||
|
|
||||||
|
jest.mock('#/state/cache/post-shadow', () => ({
|
||||||
|
dangerousGetPostShadow: jest.fn(),
|
||||||
|
updatePostShadow: jest.fn(),
|
||||||
|
}))
|
||||||
|
jest.mock('#/state/queries/bookmarks/useBookmarksQuery', () => ({
|
||||||
|
findAllPostsInQueryData: jest.fn(),
|
||||||
|
}))
|
||||||
|
jest.mock('#/state/queries/explore-feed-previews', () => ({
|
||||||
|
findAllPostsInQueryData: jest.fn(),
|
||||||
|
findPostNumberingInQueryData: jest.fn(),
|
||||||
|
}))
|
||||||
|
jest.mock('#/state/queries/notifications/feed', () => ({
|
||||||
|
findAllPostsInQueryData: jest.fn(),
|
||||||
|
}))
|
||||||
|
jest.mock('#/state/queries/post-feed', () => ({
|
||||||
|
findAllPostsInQueryData: jest.fn(),
|
||||||
|
findPostNumberingInQueryData: jest.fn(),
|
||||||
|
}))
|
||||||
|
jest.mock('#/state/queries/post-quotes', () => ({
|
||||||
|
findAllPostsInQueryData: jest.fn(),
|
||||||
|
}))
|
||||||
|
jest.mock('#/state/queries/search-posts-v2', () => ({
|
||||||
|
findAllPostsInQueryData: jest.fn(),
|
||||||
|
}))
|
||||||
|
jest.mock('#/state/queries/usePostThread', () => ({
|
||||||
|
usePostThreadContext: jest.fn(),
|
||||||
|
}))
|
||||||
|
|
||||||
|
const finders = [
|
||||||
|
findAllPostsInBookmarksQueryData,
|
||||||
|
findAllPostsInExploreFeedPreviewsQueryData,
|
||||||
|
findAllPostsInNotifsQueryData,
|
||||||
|
findAllPostsInFeedQueryData,
|
||||||
|
findAllPostsInQuoteQueryData,
|
||||||
|
findAllPostsInSearchQueryData,
|
||||||
|
]
|
||||||
|
|
||||||
|
function post(uri: string, likeCount: number) {
|
||||||
|
return {
|
||||||
|
$type: 'app.bsky.feed.defs#postView',
|
||||||
|
uri,
|
||||||
|
likeCount,
|
||||||
|
} as app.bsky.feed.defs.PostView
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('getThreadPlaceholder', () => {
|
||||||
|
const queryClient = new QueryClient()
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.resetAllMocks()
|
||||||
|
for (const finder of finders) {
|
||||||
|
jest.mocked(finder).mockImplementation(function* () {})
|
||||||
|
}
|
||||||
|
jest.mocked(findPostNumberingInFeedQueryData).mockReturnValue(undefined)
|
||||||
|
jest
|
||||||
|
.mocked(findPostNumberingInExploreFeedPreviewsQueryData)
|
||||||
|
.mockReturnValue(undefined)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('combines feed numbering with the preferred cached post', () => {
|
||||||
|
const uri = 'at://did:plc:alice/app.bsky.feed.post/1'
|
||||||
|
const notificationPost = post(uri, 4)
|
||||||
|
const feedPost = post(uri, 1)
|
||||||
|
jest.mocked(findPostNumberingInFeedQueryData).mockReturnValue({
|
||||||
|
opThreadPostIndex: 2,
|
||||||
|
opThreadPostCount: 4,
|
||||||
|
})
|
||||||
|
jest.mocked(findAllPostsInNotifsQueryData).mockImplementation(function* () {
|
||||||
|
yield notificationPost
|
||||||
|
return undefined
|
||||||
|
})
|
||||||
|
jest.mocked(findAllPostsInFeedQueryData).mockImplementation(function* () {
|
||||||
|
yield feedPost
|
||||||
|
return undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
const placeholder = getThreadPlaceholder(queryClient, uri)
|
||||||
|
|
||||||
|
expect(placeholder?.value).toMatchObject({
|
||||||
|
post: notificationPost,
|
||||||
|
opThread: true,
|
||||||
|
opThreadPostIndex: 2,
|
||||||
|
opThreadPostCount: 4,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('keeps non-numbered placeholders out of the OP thread', () => {
|
||||||
|
const uri = 'at://did:plc:alice/app.bsky.feed.post/1'
|
||||||
|
jest.mocked(findAllPostsInFeedQueryData).mockImplementation(function* () {
|
||||||
|
yield post(uri, 1)
|
||||||
|
return undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
const placeholder = getThreadPlaceholder(queryClient, uri)
|
||||||
|
|
||||||
|
expect(placeholder?.value).toMatchObject({opThread: false})
|
||||||
|
expect(placeholder?.value).not.toHaveProperty('opThreadPostIndex')
|
||||||
|
expect(placeholder?.value).not.toHaveProperty('opThreadPostCount')
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -3,14 +3,21 @@ import {type $Typed} from '@atproto/lex'
|
|||||||
import {AtUri} from '@atproto/syntax'
|
import {AtUri} from '@atproto/syntax'
|
||||||
import {type QueryClient, useQueryClient} from '@tanstack/react-query'
|
import {type QueryClient, useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
|
import {type ValidFeedPostNumbering} from '#/lib/api/feed-manip'
|
||||||
import {
|
import {
|
||||||
dangerousGetPostShadow,
|
dangerousGetPostShadow,
|
||||||
updatePostShadow,
|
updatePostShadow,
|
||||||
} from '#/state/cache/post-shadow'
|
} from '#/state/cache/post-shadow'
|
||||||
import {findAllPostsInQueryData as findAllPostsInBookmarksQueryData} from '#/state/queries/bookmarks/useBookmarksQuery'
|
import {findAllPostsInQueryData as findAllPostsInBookmarksQueryData} from '#/state/queries/bookmarks/useBookmarksQuery'
|
||||||
import {findAllPostsInQueryData as findAllPostsInExploreFeedPreviewsQueryData} from '#/state/queries/explore-feed-previews'
|
import {
|
||||||
|
findAllPostsInQueryData as findAllPostsInExploreFeedPreviewsQueryData,
|
||||||
|
findPostNumberingInQueryData as findPostNumberingInExploreFeedPreviewsQueryData,
|
||||||
|
} from '#/state/queries/explore-feed-previews'
|
||||||
import {findAllPostsInQueryData as findAllPostsInNotifsQueryData} from '#/state/queries/notifications/feed'
|
import {findAllPostsInQueryData as findAllPostsInNotifsQueryData} from '#/state/queries/notifications/feed'
|
||||||
import {findAllPostsInQueryData as findAllPostsInFeedQueryData} from '#/state/queries/post-feed'
|
import {
|
||||||
|
findAllPostsInQueryData as findAllPostsInFeedQueryData,
|
||||||
|
findPostNumberingInQueryData as findPostNumberingInFeedQueryData,
|
||||||
|
} from '#/state/queries/post-feed'
|
||||||
import {findAllPostsInQueryData as findAllPostsInQuoteQueryData} from '#/state/queries/post-quotes'
|
import {findAllPostsInQueryData as findAllPostsInQuoteQueryData} from '#/state/queries/post-quotes'
|
||||||
import {findAllPostsInQueryData as findAllPostsInSearchQueryData} from '#/state/queries/search-posts-v2'
|
import {findAllPostsInQueryData as findAllPostsInSearchQueryData} from '#/state/queries/search-posts-v2'
|
||||||
import {usePostThreadContext} from '#/state/queries/usePostThread'
|
import {usePostThreadContext} from '#/state/queries/usePostThread'
|
||||||
@@ -207,8 +214,15 @@ export function getThreadPlaceholder(
|
|||||||
queryClient: QueryClient,
|
queryClient: QueryClient,
|
||||||
uri: string,
|
uri: string,
|
||||||
): $Typed<app.bsky.unspecced.getPostThreadV2.ThreadItem> | void {
|
): $Typed<app.bsky.unspecced.getPostThreadV2.ThreadItem> | void {
|
||||||
|
const postNumbering =
|
||||||
|
findPostNumberingInFeedQueryData(queryClient, uri) ??
|
||||||
|
findPostNumberingInExploreFeedPreviewsQueryData(queryClient, uri)
|
||||||
let partial
|
let partial
|
||||||
for (let item of getThreadPlaceholderCandidates(queryClient, uri)) {
|
for (let item of getThreadPlaceholderCandidates(
|
||||||
|
queryClient,
|
||||||
|
uri,
|
||||||
|
postNumbering,
|
||||||
|
)) {
|
||||||
/*
|
/*
|
||||||
* Currently, the backend doesn't send full post info in some cases (for
|
* Currently, the backend doesn't send full post info in some cases (for
|
||||||
* example, for quoted posts). We use missing `likeCount` as a way to
|
* example, for quoted posts). We use missing `likeCount` as a way to
|
||||||
@@ -231,6 +245,7 @@ export function getThreadPlaceholder(
|
|||||||
export function* getThreadPlaceholderCandidates(
|
export function* getThreadPlaceholderCandidates(
|
||||||
queryClient: QueryClient,
|
queryClient: QueryClient,
|
||||||
uri: string,
|
uri: string,
|
||||||
|
postNumbering?: ValidFeedPostNumbering,
|
||||||
): Generator<
|
): Generator<
|
||||||
$Typed<
|
$Typed<
|
||||||
Omit<app.bsky.unspecced.getPostThreadV2.ThreadItem, 'value'> & {
|
Omit<app.bsky.unspecced.getPostThreadV2.ThreadItem, 'value'> & {
|
||||||
@@ -243,7 +258,7 @@ export function* getThreadPlaceholderCandidates(
|
|||||||
* Check post thread queries first
|
* Check post thread queries first
|
||||||
*/
|
*/
|
||||||
for (const post of findAllPostsInQueryData(queryClient, uri)) {
|
for (const post of findAllPostsInQueryData(queryClient, uri)) {
|
||||||
yield postViewToThreadPlaceholder(post)
|
yield postViewToThreadPlaceholder(post, postNumbering)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -253,25 +268,25 @@ export function* getThreadPlaceholderCandidates(
|
|||||||
* avoid a notification->post scroll jump.
|
* avoid a notification->post scroll jump.
|
||||||
*/
|
*/
|
||||||
for (let post of findAllPostsInNotifsQueryData(queryClient, uri)) {
|
for (let post of findAllPostsInNotifsQueryData(queryClient, uri)) {
|
||||||
yield postViewToThreadPlaceholder(post)
|
yield postViewToThreadPlaceholder(post, postNumbering)
|
||||||
}
|
}
|
||||||
for (let post of findAllPostsInFeedQueryData(queryClient, uri)) {
|
for (let post of findAllPostsInFeedQueryData(queryClient, uri)) {
|
||||||
yield postViewToThreadPlaceholder(post)
|
yield postViewToThreadPlaceholder(post, postNumbering)
|
||||||
}
|
}
|
||||||
for (let post of findAllPostsInQuoteQueryData(queryClient, uri)) {
|
for (let post of findAllPostsInQuoteQueryData(queryClient, uri)) {
|
||||||
yield postViewToThreadPlaceholder(post)
|
yield postViewToThreadPlaceholder(post, postNumbering)
|
||||||
}
|
}
|
||||||
for (let post of findAllPostsInSearchQueryData(queryClient, uri)) {
|
for (let post of findAllPostsInSearchQueryData(queryClient, uri)) {
|
||||||
yield postViewToThreadPlaceholder(post)
|
yield postViewToThreadPlaceholder(post, postNumbering)
|
||||||
}
|
}
|
||||||
for (let post of findAllPostsInBookmarksQueryData(queryClient, uri)) {
|
for (let post of findAllPostsInBookmarksQueryData(queryClient, uri)) {
|
||||||
yield postViewToThreadPlaceholder(post)
|
yield postViewToThreadPlaceholder(post, postNumbering)
|
||||||
}
|
}
|
||||||
for (let post of findAllPostsInExploreFeedPreviewsQueryData(
|
for (let post of findAllPostsInExploreFeedPreviewsQueryData(
|
||||||
queryClient,
|
queryClient,
|
||||||
uri,
|
uri,
|
||||||
)) {
|
)) {
|
||||||
yield postViewToThreadPlaceholder(post)
|
yield postViewToThreadPlaceholder(post, postNumbering)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import {type $Typed} from '@atproto/lex'
|
|||||||
import {AtUri} from '@atproto/syntax'
|
import {AtUri} from '@atproto/syntax'
|
||||||
import {moderatePost, type ModerationOpts} from '@bsky/sdk/moderation'
|
import {moderatePost, type ModerationOpts} from '@bsky/sdk/moderation'
|
||||||
|
|
||||||
|
import {type ValidFeedPostNumbering} from '#/lib/api/feed-manip'
|
||||||
import {makeProfileLink} from '#/lib/routes/links'
|
import {makeProfileLink} from '#/lib/routes/links'
|
||||||
import {
|
import {
|
||||||
type ApiThreadItem,
|
type ApiThreadItem,
|
||||||
@@ -156,6 +157,7 @@ export function skeleton({
|
|||||||
|
|
||||||
export function postViewToThreadPlaceholder(
|
export function postViewToThreadPlaceholder(
|
||||||
post: app.bsky.feed.defs.PostView,
|
post: app.bsky.feed.defs.PostView,
|
||||||
|
postNumbering?: ValidFeedPostNumbering,
|
||||||
): $Typed<
|
): $Typed<
|
||||||
Omit<app.bsky.unspecced.getPostThreadV2.ThreadItem, 'value'> & {
|
Omit<app.bsky.unspecced.getPostThreadV2.ThreadItem, 'value'> & {
|
||||||
value: $Typed<app.bsky.unspecced.defs.ThreadItemPost>
|
value: $Typed<app.bsky.unspecced.defs.ThreadItemPost>
|
||||||
@@ -168,7 +170,8 @@ export function postViewToThreadPlaceholder(
|
|||||||
value: {
|
value: {
|
||||||
$type: 'app.bsky.unspecced.defs#threadItemPost',
|
$type: 'app.bsky.unspecced.defs#threadItemPost',
|
||||||
post,
|
post,
|
||||||
opThread: false,
|
opThread: !!postNumbering,
|
||||||
|
...postNumbering,
|
||||||
moreParents: false,
|
moreParents: false,
|
||||||
moreReplies: 0,
|
moreReplies: 0,
|
||||||
hiddenByThreadgate: false,
|
hiddenByThreadgate: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user