Update APIs

This commit is contained in:
Eric Bailey
2025-05-25 13:05:32 -05:00
parent da4afb78d0
commit ef3baf8368
6 changed files with 42 additions and 73 deletions
+7 -12
View File
@@ -11,17 +11,13 @@ import {
HiddenReplyKind, HiddenReplyKind,
type UsePostThreadProps, type UsePostThreadProps,
} from '#/state/queries/usePostThread/types' } from '#/state/queries/usePostThread/types'
import { import {getThreadgateRecord} from '#/state/queries/usePostThread/utils'
getThreadgateRecord,
mapSortOptionsToSortID,
} from '#/state/queries/usePostThread/utils'
import {useAgent, useSession} from '#/state/session' import {useAgent, useSession} from '#/state/session'
import {useMergeThreadgateHiddenReplies} from '#/state/threadgate-hidden-replies' import {useMergeThreadgateHiddenReplies} from '#/state/threadgate-hidden-replies'
export * from '#/state/queries/usePostThread/types' export * from '#/state/queries/usePostThread/types'
export function usePostThread({ export function usePostThread({
uri,
enabled: isEnabled, enabled: isEnabled,
params, params,
state, state,
@@ -32,9 +28,8 @@ export function usePostThread({
const moderationOpts = useModerationOpts() const moderationOpts = useModerationOpts()
const mergeThreadgateHiddenReplies = useMergeThreadgateHiddenReplies() const mergeThreadgateHiddenReplies = useMergeThreadgateHiddenReplies()
const enabled = isEnabled !== false && !!uri && !!moderationOpts const enabled = isEnabled !== false && !!params.anchor && !!moderationOpts
const queryKey = createPostThreadQueryKey({ const queryKey = createPostThreadQueryKey({
uri,
params, params,
}) })
@@ -44,16 +39,17 @@ export function usePostThread({
gcTime: 0, gcTime: 0,
async queryFn() { async queryFn() {
const {data} = await agent.app.bsky.unspecced.getPostThreadV2({ const {data} = await agent.app.bsky.unspecced.getPostThreadV2({
anchor: uri!, anchor: params.anchor!,
branchingFactor: params.view === 'linear' ? 1 : 3, // 100 TODO branchingFactor: params.view === 'linear' ? 1 : 3, // 100 TODO
below: 3, below: 3,
sorting: mapSortOptionsToSortID(params.sort), sort: params.sort,
prioritizeFollowedUsers: params.prioritizeFollowedUsers,
}) })
return data return data
}, },
placeholderData() { placeholderData() {
if (!uri) return if (!params.anchor) return
const placeholder = getThreadPlaceholder(qc, uri) const placeholder = getThreadPlaceholder(qc, params.anchor)
if (placeholder) { if (placeholder) {
return {thread: [placeholder]} return {thread: [placeholder]}
} }
@@ -96,7 +92,6 @@ export function usePostThread({
return { return {
...query, ...query,
data: { data: {
anchorIndex: items.findIndex(i => Boolean(i.ui?.isAnchor)),
items, items,
threadgate: query.data?.threadgate, threadgate: query.data?.threadgate,
}, },
+10 -6
View File
@@ -1,7 +1,6 @@
import { import {
APP_BSKY_UNSPECCED,
AtUri,
AppBskyUnspeccedGetPostThreadV2, AppBskyUnspeccedGetPostThreadV2,
AtUri,
type ModerationDecision, type ModerationDecision,
type ModerationOpts, type ModerationOpts,
} from '@atproto/api' } from '@atproto/api'
@@ -30,7 +29,11 @@ export function flatten(
if (item.type === 'threadPost') { if (item.type === 'threadPost') {
// TODO should not insert if not found post etc // TODO should not insert if not found post etc
if (item.ui.isAnchor && hasSession && !item.value.post.viewer?.replyDisabled) { if (
item.ui.isAnchor &&
hasSession &&
!item.value.post.viewer?.replyDisabled
) {
flattened.splice(i + 1, 0, { flattened.splice(i + 1, 0, {
type: 'replyComposer', type: 'replyComposer',
key: 'replyComposer', key: 'replyComposer',
@@ -39,10 +42,10 @@ export function flatten(
const prev = unhydratedReplyIntervals[unhydratedReplyIntervals.length - 1] const prev = unhydratedReplyIntervals[unhydratedReplyIntervals.length - 1]
if (item.annotations.has(APP_BSKY_UNSPECCED.GetPostThreadV2HasMoreReplies)) { if (item.value.moreReplies > 0) {
unhydratedReplyIntervals.push({ unhydratedReplyIntervals.push({
item, item,
replyCount: item.value.post.replyCount || 0, replyCount: item.value.moreReplies,
}) })
} }
@@ -50,7 +53,8 @@ export function flatten(
* If direct child of previous item with `hasMoreReplies`, subtract * If direct child of previous item with `hasMoreReplies`, subtract
*/ */
if (prev && item.depth === prev.item.depth + 1) { if (prev && item.depth === prev.item.depth + 1) {
prev.replyCount = Math.max(0, prev.replyCount - 1) // TODO test if we need this
// prev.replyCount = Math.max(0, prev.replyCount - 1)
} }
if (prev && item.depth <= prev.item.depth) { if (prev && item.depth <= prev.item.depth) {
+7 -15
View File
@@ -1,28 +1,26 @@
import { import {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
type APP_BSKY_UNSPECCED,
type AtUri,
type AppBskyFeedDefs, type AppBskyFeedDefs,
type AppBskyFeedPost, type AppBskyFeedPost,
type AppBskyUnspeccedGetPostThreadV2, type AppBskyUnspeccedGetPostThreadV2,
type BskyThreadViewPreference, type AtUri,
type ModerationDecision, type ModerationDecision,
} from '@atproto/api' } from '@atproto/api'
export const postThreadQueryKeyRoot = 'getPostThreadV2' as const export const postThreadQueryKeyRoot = 'getPostThreadV2' as const
export const createPostThreadQueryKey = ( export const createPostThreadQueryKey = (
props: Pick<UsePostThreadProps, 'uri' | 'params'>, props: Pick<UsePostThreadProps, 'params'>,
) => [postThreadQueryKeyRoot, props] as const ) => [postThreadQueryKeyRoot, props] as const
export type PostThreadParams = { export type PostThreadParams = Pick<
AppBskyUnspeccedGetPostThreadV2.QueryParams,
'sort' | 'prioritizeFollowedUsers'
> & {
anchor?: string
view: 'tree' | 'linear' view: 'tree' | 'linear'
sort: 'top' | 'oldest' | 'newest' | string
prioritizeFollows: BskyThreadViewPreference['prioritizeFollowedUsers']
} }
export type UsePostThreadProps = { export type UsePostThreadProps = {
uri?: string
enabled?: boolean enabled?: boolean
params: PostThreadParams params: PostThreadParams
state: { state: {
@@ -47,12 +45,6 @@ export type Slice =
} }
} }
moderation: ModerationDecision moderation: ModerationDecision
/**
* Reference via {@link APP_BSKY_UNSPECCED}
*/
annotations: Set<
AppBskyUnspeccedGetPostThreadV2.ThreadItemPost['annotations'][number]
>
ui: { ui: {
isAnchor: boolean isAnchor: boolean
showParentReplyLine: boolean showParentReplyLine: boolean
-15
View File
@@ -1,5 +1,4 @@
import { import {
APP_BSKY_UNSPECCED,
type AppBskyFeedDefs, type AppBskyFeedDefs,
AppBskyFeedPost, AppBskyFeedPost,
AppBskyFeedThreadgate, AppBskyFeedThreadgate,
@@ -7,22 +6,8 @@ import {
AtUri, AtUri,
} from '@atproto/api' } from '@atproto/api'
import {type PostThreadParams} from '#/state/queries/usePostThread/types'
import * as bsky from '#/types/bsky' import * as bsky from '#/types/bsky'
export function mapSortOptionsToSortID(sort: PostThreadParams['sort']) {
switch (sort) {
case 'top':
return APP_BSKY_UNSPECCED.GetPostThreadV2Top
case 'oldest':
return APP_BSKY_UNSPECCED.GetPostThreadV2Oldest
case 'newest':
return APP_BSKY_UNSPECCED.GetPostThreadV2Newest
default:
return APP_BSKY_UNSPECCED.GetPostThreadV2Top
}
}
export function getThreadgateRecord( export function getThreadgateRecord(
view: AppBskyUnspeccedGetPostThreadV2.OutputSchema['threadgate'], view: AppBskyUnspeccedGetPostThreadV2.OutputSchema['threadgate'],
) { ) {
+5 -2
View File
@@ -90,7 +90,6 @@ export function threadPost({
record: AppBskyFeedPost.Record record: AppBskyFeedPost.Record
}, },
}, },
annotations: new Set(value.annotations),
moderation: moderatePost(value.post, moderationOpts), moderation: moderatePost(value.post, moderationOpts),
ui: { ui: {
isAnchor: depth === 0, isAnchor: depth === 0,
@@ -114,7 +113,11 @@ export function postViewToThreadPlaceholder(
value: { value: {
$type: 'app.bsky.unspecced.getPostThreadV2#threadItemPost', $type: 'app.bsky.unspecced.getPostThreadV2#threadItemPost',
post, post,
annotations: [], hiddenByThreadgate: false,
opThread: false,
moreParents: false,
moreReplies: 0,
mutedByViewer: false,
}, },
} }
} }
+12 -22
View File
@@ -5,24 +5,25 @@ import {useLingui} from '@lingui/react'
import {useFocusEffect} from '@react-navigation/native' import {useFocusEffect} from '@react-navigation/native'
import {HITSLOP_10} from '#/lib/constants' import {HITSLOP_10} from '#/lib/constants'
// import {PostThread as PostThreadComponent} from '#/view/com/post-thread/PostThread'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
import {makeProfileLink} from '#/lib/routes/links'
import { import {
type CommonNavigatorParams, type CommonNavigatorParams,
type NativeStackScreenProps, type NativeStackScreenProps,
} from '#/lib/routes/types' } from '#/lib/routes/types'
import {ScrollProvider} from '#/lib/ScrollContext'
import {cleanError} from '#/lib/strings/errors' import {cleanError} from '#/lib/strings/errors'
import {makeRecordUri} from '#/lib/strings/url-helpers' import {makeRecordUri} from '#/lib/strings/url-helpers'
import {isNative} from '#/platform/detection' import {isNative} from '#/platform/detection'
import {useSetMinimalShellMode} from '#/state/shell'
import {PostThread as PostThreadComponent} from '#/view/com/post-thread/PostThread'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
import {ScrollProvider} from '#/lib/ScrollContext'
import {usePreferencesQuery} from '#/state/queries/preferences' import {usePreferencesQuery} from '#/state/queries/preferences'
import { import {
HiddenReplyKind, HiddenReplyKind,
type Slice, type Slice,
usePostThread, usePostThread,
} from '#/state/queries/usePostThread' } from '#/state/queries/usePostThread'
import {useSetMinimalShellMode} from '#/state/shell'
import {type OnPostSuccessData} from '#/state/shell/composer' import {type OnPostSuccessData} from '#/state/shell/composer'
import {PostThreadComposePrompt} from '#/view/com/post-thread/PostThreadComposePrompt' import {PostThreadComposePrompt} from '#/view/com/post-thread/PostThreadComposePrompt'
import {PostThreadItem} from '#/view/com/post-thread/PostThreadItem' import {PostThreadItem} from '#/view/com/post-thread/PostThreadItem'
@@ -30,21 +31,14 @@ import {PostThreadShowHiddenReplies} from '#/view/com/post-thread/PostThreadShow
import {List, type ListMethods} from '#/view/com/util/List' import {List, type ListMethods} from '#/view/com/util/List'
import {atoms as a, useBreakpoints, useTheme, web} from '#/alf' import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
import {Button, ButtonIcon} from '#/components/Button' import {Button, ButtonIcon} from '#/components/Button'
import {Link} from '#/components/Link'
import {makeProfileLink} from '#/lib/routes/links'
import {SettingsSliderVertical_Stroke2_Corner0_Rounded as SettingsSlider} from '#/components/icons/SettingsSlider'
import {CirclePlus_Stroke2_Corner0_Rounded as CirclePlus} from '#/components/icons/CirclePlus' import {CirclePlus_Stroke2_Corner0_Rounded as CirclePlus} from '#/components/icons/CirclePlus'
import {SettingsSliderVertical_Stroke2_Corner0_Rounded as SettingsSlider} from '#/components/icons/SettingsSlider'
import * as Layout from '#/components/Layout' import * as Layout from '#/components/Layout'
import {Link} from '#/components/Link'
import {ListFooter} from '#/components/Lists' import {ListFooter} from '#/components/Lists'
import * as Menu from '#/components/Menu' import * as Menu from '#/components/Menu'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
const MAINTAIN_VISIBLE_CONTENT_POSITION = {
// We don't insert any elements before the root row while loading.
// So the row we want to use as the scroll anchor is the first row.
minIndexForVisible: 0,
}
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostThread'> type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostThread'>
export function PostThreadScreen({route}: Props) { export function PostThreadScreen({route}: Props) {
const setMinimalShellMode = useSetMinimalShellMode() const setMinimalShellMode = useSetMinimalShellMode()
@@ -144,12 +138,12 @@ export function Inner({uri}: {uri: string | undefined}) {
>(new Set()) >(new Set())
const {isFetching, error, data, refetch, insertReplies} = usePostThread({ const {isFetching, error, data, refetch, insertReplies} = usePostThread({
uri,
enabled: isThreadPreferencesLoaded, enabled: isThreadPreferencesLoaded,
params: { params: {
anchor: uri,
sort: sortReplies, sort: sortReplies,
view: treeViewEnabled ? 'tree' : 'linear', view: treeViewEnabled ? 'tree' : 'linear',
prioritizeFollows: prioritizeFollowedUsers, prioritizeFollowedUsers,
}, },
state: { state: {
shownHiddenReplyKinds, shownHiddenReplyKinds,
@@ -236,9 +230,7 @@ export function Inner({uri}: {uri: string | undefined}) {
hasMore={false} // TODO need to replace this entirely hasMore={false} // TODO need to replace this entirely
showChildReplyLine={item.ui.showChildReplyLine} showChildReplyLine={item.ui.showChildReplyLine}
showParentReplyLine={item.ui.showParentReplyLine} showParentReplyLine={item.ui.showParentReplyLine}
hasPrecedingItem={ hasPrecedingItem={item.ui.showParentReplyLine} // !!hasUnrevealedParents // TODO
item.ui.showParentReplyLine
} // !!hasUnrevealedParents // TODO
overrideBlur={ overrideBlur={
shownHiddenReplyKinds.has(HiddenReplyKind.Muted) && item.depth > 0 shownHiddenReplyKinds.has(HiddenReplyKind.Muted) && item.depth > 0
} }
@@ -358,8 +350,6 @@ export function Inner({uri}: {uri: string | undefined}) {
return null return null
} }
console.log('PostThreadScreen', data?.anchorIndex)
return ( return (
<> <>
<Layout.Header.Outer headerRef={headerRef}> <Layout.Header.Outer headerRef={headerRef}>
@@ -400,7 +390,7 @@ export function Inner({uri}: {uri: string | undefined}) {
*/ */
maintainVisibleContentPosition={ maintainVisibleContentPosition={
isNative // && hasParents // TODO not sure we need this isNative // && hasParents // TODO not sure we need this
? { minIndexForVisible: 0 } // MAINTAIN_VISIBLE_CONTENT_POSITION ? {minIndexForVisible: 0} // MAINTAIN_VISIBLE_CONTENT_POSITION
: undefined : undefined
} }
desktopFixedHeight desktopFixedHeight