Show replies in context of their threads (#4871)
* Don't reconstruct threads from separate posts * Remove post-level dedupe for now * Change repost dedupe condition to look just at length * Delete unused isThread * Delete another isThread field It is now meaningless because there's nothing special about author threads. * Narrow down slice item shape so it does not need reply * Consolidate slice validation criteria in one place * Show replies in context * Make fallback marker work * Remove misleading and now-unused property It was called rootUri but it was actually the leaf URI. Regardless, it's not used anymore. * Add by-thread dedupe to non-author feeds * Add post-level dedupe * Always count from the start This is easier to think about. * Only tuner state need to be untouched on dry run * Account for threads in reply filtering * Remove repost deduping This is already being taken care of by item-level deduping. It's also now wrong and removing too much (since it wasn't filtering for reposts directly). * Calculate rootUri correctly * Apply Following settings to all lists * Don't dedupe intentional reposts by thread * Show reply parent when ambiguous * Explicitly remove orphaned replies from following/lists * Fix thread dedupe to work across pages * Mark grandparent-blocked as orphaned * Guard tuner state change by dryRun * Remove dead code * Don't dedupe feedgen threads * Revert "Apply Following settings to all lists" This reverts commit aff86be6d37b60cc5d0ac38f22c31a4808342cf4. Let's not do this yet and have a bit more discussion. This is a chunky change already. * Reason belongs to a slice, not item * Logically feedContext belongs to the slice * Update comment to reflect latest behavior
This commit is contained in:
parent
18b423396b
commit
74b0318d89
8 changed files with 279 additions and 300 deletions
|
@ -123,7 +123,7 @@ export function useFeedFeedback(feed: FeedDescriptor, hasSession: boolean) {
|
|||
toString({
|
||||
item: postItem.uri,
|
||||
event: 'app.bsky.feed.defs#interactionSeen',
|
||||
feedContext: postItem.feedContext,
|
||||
feedContext: slice.feedContext,
|
||||
}),
|
||||
)
|
||||
sendToFeed()
|
||||
|
|
|
@ -19,20 +19,15 @@ export function useFeedTuners(feedDesc: FeedDescriptor) {
|
|||
}
|
||||
}
|
||||
if (feedDesc.startsWith('feedgen')) {
|
||||
return [
|
||||
FeedTuner.dedupReposts,
|
||||
FeedTuner.preferredLangOnly(langPrefs.contentLanguages),
|
||||
]
|
||||
return [FeedTuner.preferredLangOnly(langPrefs.contentLanguages)]
|
||||
}
|
||||
if (feedDesc.startsWith('list')) {
|
||||
const feedTuners = []
|
||||
|
||||
let feedTuners = []
|
||||
if (feedDesc.endsWith('|as_following')) {
|
||||
// Same as Following tuners below, copypaste for now.
|
||||
feedTuners.push(FeedTuner.removeOrphans)
|
||||
if (preferences?.feedViewPrefs.hideReposts) {
|
||||
feedTuners.push(FeedTuner.removeReposts)
|
||||
} else {
|
||||
feedTuners.push(FeedTuner.dedupReposts)
|
||||
}
|
||||
if (preferences?.feedViewPrefs.hideReplies) {
|
||||
feedTuners.push(FeedTuner.removeReplies)
|
||||
|
@ -46,18 +41,15 @@ export function useFeedTuners(feedDesc: FeedDescriptor) {
|
|||
if (preferences?.feedViewPrefs.hideQuotePosts) {
|
||||
feedTuners.push(FeedTuner.removeQuotePosts)
|
||||
}
|
||||
} else {
|
||||
feedTuners.push(FeedTuner.dedupReposts)
|
||||
feedTuners.push(FeedTuner.dedupThreads)
|
||||
}
|
||||
return feedTuners
|
||||
}
|
||||
if (feedDesc === 'following') {
|
||||
const feedTuners = []
|
||||
const feedTuners = [FeedTuner.removeOrphans]
|
||||
|
||||
if (preferences?.feedViewPrefs.hideReposts) {
|
||||
feedTuners.push(FeedTuner.removeReposts)
|
||||
} else {
|
||||
feedTuners.push(FeedTuner.dedupReposts)
|
||||
}
|
||||
if (preferences?.feedViewPrefs.hideReplies) {
|
||||
feedTuners.push(FeedTuner.removeReplies)
|
||||
|
@ -71,6 +63,7 @@ export function useFeedTuners(feedDesc: FeedDescriptor) {
|
|||
if (preferences?.feedViewPrefs.hideQuotePosts) {
|
||||
feedTuners.push(FeedTuner.removeQuotePosts)
|
||||
}
|
||||
feedTuners.push(FeedTuner.dedupThreads)
|
||||
|
||||
return feedTuners
|
||||
}
|
||||
|
|
|
@ -77,11 +77,6 @@ export interface FeedPostSliceItem {
|
|||
uri: string
|
||||
post: AppBskyFeedDefs.PostView
|
||||
record: AppBskyFeedPost.Record
|
||||
reason?:
|
||||
| AppBskyFeedDefs.ReasonRepost
|
||||
| ReasonFeedSource
|
||||
| {[k: string]: unknown; $type: string}
|
||||
feedContext: string | undefined
|
||||
moderation: ModerationDecision
|
||||
parentAuthor?: AppBskyActorDefs.ProfileViewBasic
|
||||
isParentBlocked?: boolean
|
||||
|
@ -90,9 +85,14 @@ export interface FeedPostSliceItem {
|
|||
export interface FeedPostSlice {
|
||||
_isFeedPostSlice: boolean
|
||||
_reactKey: string
|
||||
rootUri: string
|
||||
isThread: boolean
|
||||
items: FeedPostSliceItem[]
|
||||
isIncompleteThread: boolean
|
||||
isFallbackMarker: boolean
|
||||
feedContext: string | undefined
|
||||
reason?:
|
||||
| AppBskyFeedDefs.ReasonRepost
|
||||
| ReasonFeedSource
|
||||
| {[k: string]: unknown; $type: string}
|
||||
}
|
||||
|
||||
export interface FeedPageUnselected {
|
||||
|
@ -313,53 +313,22 @@ export function usePostFeedQuery(
|
|||
const feedPostSlice: FeedPostSlice = {
|
||||
_reactKey: slice._reactKey,
|
||||
_isFeedPostSlice: true,
|
||||
rootUri: slice.uri,
|
||||
isThread:
|
||||
slice.items.length > 1 &&
|
||||
slice.items.every(
|
||||
item =>
|
||||
item.post.author.did ===
|
||||
slice.items[0].post.author.did,
|
||||
),
|
||||
items: slice.items
|
||||
.map((item, i) => {
|
||||
if (
|
||||
AppBskyFeedPost.isRecord(item.post.record) &&
|
||||
AppBskyFeedPost.validateRecord(item.post.record)
|
||||
.success
|
||||
) {
|
||||
const parent = item.reply?.parent
|
||||
let parentAuthor:
|
||||
| AppBskyActorDefs.ProfileViewBasic
|
||||
| undefined
|
||||
if (AppBskyFeedDefs.isPostView(parent)) {
|
||||
parentAuthor = parent.author
|
||||
}
|
||||
if (!parentAuthor) {
|
||||
parentAuthor =
|
||||
slice.items[i + 1]?.reply?.grandparentAuthor
|
||||
}
|
||||
const replyRef = item.reply
|
||||
const isParentBlocked = AppBskyFeedDefs.isBlockedPost(
|
||||
replyRef?.parent,
|
||||
)
|
||||
|
||||
const feedPostSliceItem: FeedPostSliceItem = {
|
||||
_reactKey: `${slice._reactKey}-${i}-${item.post.uri}`,
|
||||
uri: item.post.uri,
|
||||
post: item.post,
|
||||
record: item.post.record,
|
||||
reason: slice.reason,
|
||||
feedContext: slice.feedContext,
|
||||
moderation: moderations[i],
|
||||
parentAuthor,
|
||||
isParentBlocked,
|
||||
}
|
||||
return feedPostSliceItem
|
||||
}
|
||||
return undefined
|
||||
})
|
||||
.filter(n => !!n),
|
||||
isIncompleteThread: slice.isIncompleteThread,
|
||||
isFallbackMarker: slice.isFallbackMarker,
|
||||
feedContext: slice.feedContext,
|
||||
reason: slice.reason,
|
||||
items: slice.items.map((item, i) => {
|
||||
const feedPostSliceItem: FeedPostSliceItem = {
|
||||
_reactKey: `${slice._reactKey}-${i}-${item.post.uri}`,
|
||||
uri: item.post.uri,
|
||||
post: item.post,
|
||||
record: item.record,
|
||||
moderation: moderations[i],
|
||||
parentAuthor: item.parentAuthor,
|
||||
isParentBlocked: item.isParentBlocked,
|
||||
}
|
||||
return feedPostSliceItem
|
||||
}),
|
||||
}
|
||||
return feedPostSlice
|
||||
})
|
||||
|
@ -442,7 +411,6 @@ export async function pollLatest(page: FeedPage | undefined) {
|
|||
if (post) {
|
||||
const slices = page.tuner.tune([post], {
|
||||
dryRun: true,
|
||||
maintainOrder: true,
|
||||
})
|
||||
if (slices[0]) {
|
||||
return true
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue