Update types
This commit is contained in:
@@ -114,6 +114,11 @@ export function Inner({uri}: {uri: string | undefined}) {
|
||||
const anchorOffsetTop = anchorElement.getBoundingClientRect().top
|
||||
const headerHeight = headerElement.getBoundingClientRect().height
|
||||
const scrollPosition = anchorOffsetTop - headerHeight
|
||||
console.log({
|
||||
anchorOffsetTop,
|
||||
headerHeight,
|
||||
scrollPosition,
|
||||
})
|
||||
/*
|
||||
* If scroll position is negative, it means the anchor post is above the
|
||||
* top of the screen, meaning the user scrolled the list. In that case,
|
||||
@@ -292,7 +297,16 @@ export function Inner({uri}: {uri: string | undefined}) {
|
||||
return (
|
||||
<PostThreadShowHiddenReplies
|
||||
type="hidden"
|
||||
onPress={() => item.onLoad()}
|
||||
onPress={() => {
|
||||
item.onLoad()
|
||||
/*
|
||||
* Bit of a hack. This resets the ref value for the anchor so that
|
||||
* the next time `onContentSizeChangeWebOnly` fires, it won't
|
||||
* adjust scroll. However, on the next render cycle, it will, which
|
||||
* will give us time to insert the skeleton state and handle scroll.
|
||||
*/
|
||||
anchorRef.current = null
|
||||
}}
|
||||
/>
|
||||
)
|
||||
} else if (item.type === 'skeleton') {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {useCallback, useMemo, useRef, useState} from 'react'
|
||||
import {useQuery, useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {wait} from '#/lib/async/wait'
|
||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||
import {
|
||||
createCacheMutator,
|
||||
@@ -8,8 +9,8 @@ import {
|
||||
} from '#/state/queries/usePostThread/queryCache'
|
||||
import {traverse} from '#/state/queries/usePostThread/traversal'
|
||||
import {
|
||||
createPostThreadQueryKey,
|
||||
createPostThreadHiddenQueryKey,
|
||||
createPostThreadQueryKey,
|
||||
type ThreadItem,
|
||||
type UsePostThreadProps,
|
||||
} from '#/state/queries/usePostThread/types'
|
||||
@@ -39,13 +40,16 @@ export function usePostThread({
|
||||
queryKey,
|
||||
gcTime: 0,
|
||||
async queryFn() {
|
||||
const {data} = await agent.app.bsky.unspecced.getPostThreadV2({
|
||||
anchor: params.anchor!,
|
||||
branchingFactor: params.view === 'linear' ? 1 : undefined,
|
||||
below: 4,
|
||||
sort: params.sort,
|
||||
prioritizeFollowedUsers: params.prioritizeFollowedUsers,
|
||||
})
|
||||
const {data} = await wait(
|
||||
400,
|
||||
agent.app.bsky.unspecced.getPostThreadV2({
|
||||
anchor: params.anchor!,
|
||||
branchingFactor: params.view === 'linear' ? 1 : undefined,
|
||||
below: 4,
|
||||
sort: params.sort,
|
||||
prioritizeFollowedUsers: params.prioritizeFollowedUsers,
|
||||
}),
|
||||
)
|
||||
return data
|
||||
},
|
||||
placeholderData() {
|
||||
@@ -84,22 +88,29 @@ export function usePostThread({
|
||||
const [hiddenReplies, setHiddenReplies] = useState<ThreadItem[]>([])
|
||||
const loadHiddenReplies = useCallback(async () => {
|
||||
setShowHiddenReplies(true)
|
||||
setHiddenReplies(Array.from({length: 2}).map((_, i) => ({
|
||||
type: 'skeleton',
|
||||
key: `${params.anchor!}-reply-${i}`,
|
||||
item: 'reply',
|
||||
})))
|
||||
setHiddenReplies(
|
||||
Array.from({length: 2}).map((_, i) => ({
|
||||
type: 'skeleton',
|
||||
key: `${params.anchor!}-reply-${i}`,
|
||||
item: 'reply',
|
||||
})),
|
||||
)
|
||||
const queryParams = {
|
||||
anchor: params.anchor!,
|
||||
prioritizeFollowedUsers: params.prioritizeFollowedUsers,
|
||||
}
|
||||
const data = await qc.fetchQuery({
|
||||
queryKey: createPostThreadHiddenQueryKey(queryParams),
|
||||
async queryFn() {
|
||||
const {data} = await agent.app.bsky.unspecced.getPostThreadHiddenV2(queryParams)
|
||||
return data.thread || []
|
||||
},
|
||||
})
|
||||
const data = await wait(
|
||||
400,
|
||||
qc.fetchQuery({
|
||||
queryKey: createPostThreadHiddenQueryKey(queryParams),
|
||||
async queryFn() {
|
||||
const {data} = await agent.app.bsky.unspecced.getPostThreadHiddenV2(
|
||||
queryParams,
|
||||
)
|
||||
return data.thread || []
|
||||
},
|
||||
}),
|
||||
)
|
||||
const items = traverse(data || [], {
|
||||
threadgateHiddenReplies: mergeThreadgateHiddenReplies(
|
||||
query.data?.threadgate?.record,
|
||||
@@ -113,7 +124,17 @@ export function usePostThread({
|
||||
loadHiddenReplies,
|
||||
})
|
||||
setHiddenReplies(items)
|
||||
}, [params, setShowHiddenReplies])
|
||||
}, [
|
||||
agent,
|
||||
params,
|
||||
hasSession,
|
||||
mergeThreadgateHiddenReplies,
|
||||
moderationOpts,
|
||||
qc,
|
||||
query.data?.threadgate?.record,
|
||||
showHiddenReplies,
|
||||
setShowHiddenReplies,
|
||||
])
|
||||
|
||||
const items = useMemo(() => {
|
||||
const results = traverse(query.data?.thread || [], {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {
|
||||
type $Typed,
|
||||
type AppBskyFeedDefs,
|
||||
AppBskyUnspeccedGetPostThreadV2,
|
||||
AppBskyUnspeccedDefs,
|
||||
type AppBskyUnspeccedGetPostThreadV2,
|
||||
AtUri,
|
||||
} from '@atproto/api'
|
||||
import {type QueryClient} from '@tanstack/react-query'
|
||||
@@ -35,7 +35,7 @@ export function createCacheMutator({
|
||||
return {
|
||||
insertReplies(
|
||||
parentUri: string,
|
||||
replies: AppBskyUnspeccedDefs.ThreadItem[],
|
||||
replies: AppBskyUnspeccedGetPostThreadV2.ThreadItem[],
|
||||
) {
|
||||
queryClient.setQueryData<AppBskyUnspeccedGetPostThreadV2.OutputSchema>(
|
||||
queryKey,
|
||||
@@ -46,11 +46,7 @@ export function createCacheMutator({
|
||||
|
||||
for (let i = 0; i < thread.length; i++) {
|
||||
const existingParent = thread[i]
|
||||
if (
|
||||
!AppBskyUnspeccedDefs.isThreadItemPost(
|
||||
existingParent.value,
|
||||
)
|
||||
)
|
||||
if (!AppBskyUnspeccedDefs.isThreadItemPost(existingParent.value))
|
||||
continue
|
||||
if (existingParent.uri !== parentUri) continue
|
||||
|
||||
@@ -68,12 +64,11 @@ export function createCacheMutator({
|
||||
const isEndOfReplyChain =
|
||||
!nextItem || nextItem.depth <= existingParent.depth
|
||||
const firstReply = replies.at(0)
|
||||
const opIsReplier =
|
||||
AppBskyUnspeccedDefs.isThreadItemPost(
|
||||
firstReply?.value,
|
||||
)
|
||||
? opDid === firstReply.value.post.author.did
|
||||
: false
|
||||
const opIsReplier = AppBskyUnspeccedDefs.isThreadItemPost(
|
||||
firstReply?.value,
|
||||
)
|
||||
? opDid === firstReply.value.post.author.did
|
||||
: false
|
||||
|
||||
/*
|
||||
* Always insert replies if the following conditions are met.
|
||||
@@ -119,7 +114,7 @@ export function createCacheMutator({
|
||||
* Unused atm, post shadow does the trick, but it would be nice to clean up
|
||||
* the whole sub-tree on deletes.
|
||||
*/
|
||||
deletePost(post: AppBskyUnspeccedDefs.ThreadItem) {
|
||||
deletePost(post: AppBskyUnspeccedGetPostThreadV2.ThreadItem) {
|
||||
queryClient.setQueryData<AppBskyUnspeccedGetPostThreadV2.OutputSchema>(
|
||||
queryKey,
|
||||
queryData => {
|
||||
@@ -129,8 +124,7 @@ export function createCacheMutator({
|
||||
|
||||
for (let i = 0; i < thread.length; i++) {
|
||||
const existingPost = thread[i]
|
||||
if (!AppBskyUnspeccedDefs.isThreadItemPost(post.value))
|
||||
continue
|
||||
if (!AppBskyUnspeccedDefs.isThreadItemPost(post.value)) continue
|
||||
|
||||
if (existingPost.uri === post.uri) {
|
||||
const branch = getBranch(thread, i, existingPost.depth)
|
||||
@@ -152,7 +146,7 @@ export function createCacheMutator({
|
||||
export function getThreadPlaceholder(
|
||||
queryClient: QueryClient,
|
||||
uri: string,
|
||||
): $Typed<AppBskyUnspeccedDefs.ThreadItem> | void {
|
||||
): $Typed<AppBskyUnspeccedGetPostThreadV2.ThreadItem> | void {
|
||||
let partial
|
||||
for (let item of getThreadPlaceholderCandidates(queryClient, uri)) {
|
||||
/*
|
||||
@@ -179,7 +173,7 @@ export function* getThreadPlaceholderCandidates(
|
||||
uri: string,
|
||||
): Generator<
|
||||
$Typed<
|
||||
Omit<AppBskyUnspeccedDefs.ThreadItem, 'value'> & {
|
||||
Omit<AppBskyUnspeccedGetPostThreadV2.ThreadItem, 'value'> & {
|
||||
value: $Typed<AppBskyUnspeccedDefs.ThreadItemPost>
|
||||
}
|
||||
>,
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
} from '@atproto/api'
|
||||
|
||||
import {
|
||||
type ApiThreadItem,
|
||||
type PostThreadParams,
|
||||
type ThreadItem,
|
||||
type TraversalMetadata,
|
||||
@@ -18,7 +19,7 @@ import {
|
||||
import * as views from '#/state/queries/usePostThread/views'
|
||||
|
||||
export function traverse(
|
||||
thread: AppBskyUnspeccedDefs.ThreadItem[],
|
||||
thread: ApiThreadItem[],
|
||||
{
|
||||
threadgateHiddenReplies,
|
||||
moderationOpts,
|
||||
@@ -67,19 +68,11 @@ export function traverse(
|
||||
* _up_ from there.
|
||||
*/
|
||||
} else if (item.depth === 0) {
|
||||
if (
|
||||
AppBskyUnspeccedDefs.isThreadItemNoUnauthenticated(
|
||||
item.value,
|
||||
)
|
||||
) {
|
||||
if (AppBskyUnspeccedDefs.isThreadItemNoUnauthenticated(item.value)) {
|
||||
items.push(views.threadPostNoUnauthenticated(item))
|
||||
} else if (
|
||||
AppBskyUnspeccedDefs.isThreadItemNotFound(item.value)
|
||||
) {
|
||||
} else if (AppBskyUnspeccedDefs.isThreadItemNotFound(item.value)) {
|
||||
items.push(views.threadPostNotFound(item))
|
||||
} else if (
|
||||
AppBskyUnspeccedDefs.isThreadItemBlocked(item.value)
|
||||
) {
|
||||
} else if (AppBskyUnspeccedDefs.isThreadItemBlocked(item.value)) {
|
||||
items.push(views.threadPostBlocked(item))
|
||||
} else if (AppBskyUnspeccedDefs.isThreadItemPost(item.value)) {
|
||||
const post = views.threadPost({
|
||||
@@ -94,25 +87,17 @@ export function traverse(
|
||||
const parent = thread[pi]
|
||||
|
||||
if (
|
||||
AppBskyUnspeccedDefs.isThreadItemNoUnauthenticated(
|
||||
parent.value,
|
||||
)
|
||||
AppBskyUnspeccedDefs.isThreadItemNoUnauthenticated(parent.value)
|
||||
) {
|
||||
items.unshift(views.threadPostNoUnauthenticated(parent))
|
||||
break parentTraversal
|
||||
} else if (
|
||||
AppBskyUnspeccedDefs.isThreadItemNotFound(parent.value)
|
||||
) {
|
||||
} else if (AppBskyUnspeccedDefs.isThreadItemNotFound(parent.value)) {
|
||||
items.unshift(views.threadPostNotFound(parent))
|
||||
break parentTraversal
|
||||
} else if (
|
||||
AppBskyUnspeccedDefs.isThreadItemBlocked(parent.value)
|
||||
) {
|
||||
} else if (AppBskyUnspeccedDefs.isThreadItemBlocked(parent.value)) {
|
||||
items.unshift(views.threadPostBlocked(parent))
|
||||
break parentTraversal
|
||||
} else if (
|
||||
AppBskyUnspeccedDefs.isThreadItemPost(parent.value)
|
||||
) {
|
||||
} else if (AppBskyUnspeccedDefs.isThreadItemPost(parent.value)) {
|
||||
items.unshift(
|
||||
views.threadPost({
|
||||
uri: parent.uri,
|
||||
@@ -131,9 +116,7 @@ export function traverse(
|
||||
* we could.
|
||||
*/
|
||||
const shouldBreak =
|
||||
AppBskyUnspeccedDefs.isThreadItemNoUnauthenticated(
|
||||
item.value,
|
||||
) ||
|
||||
AppBskyUnspeccedDefs.isThreadItemNoUnauthenticated(item.value) ||
|
||||
AppBskyUnspeccedDefs.isThreadItemNotFound(item.value) ||
|
||||
AppBskyUnspeccedDefs.isThreadItemBlocked(item.value)
|
||||
|
||||
@@ -193,9 +176,7 @@ export function traverse(
|
||||
for (let ci = startIndex; ci <= branch.end; ci++) {
|
||||
const child = thread[ci]
|
||||
|
||||
if (
|
||||
AppBskyUnspeccedDefs.isThreadItemPost(child.value)
|
||||
) {
|
||||
if (AppBskyUnspeccedDefs.isThreadItemPost(child.value)) {
|
||||
const childParentMetadata = metadatas.get(
|
||||
getPostRecord(child.value.post).reply?.parent?.uri || '',
|
||||
)
|
||||
@@ -432,7 +413,7 @@ export function traverse(
|
||||
* const { start: 1, end: 3 } = getBranch(items, 1, 1)
|
||||
*/
|
||||
export function getBranch(
|
||||
thread: AppBskyUnspeccedDefs.ThreadItem[],
|
||||
thread: ApiThreadItem[],
|
||||
branchStartIndex: number,
|
||||
branchStartDepth: number,
|
||||
) {
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import {
|
||||
type AppBskyFeedDefs,
|
||||
type AppBskyFeedPost,
|
||||
type AppBskyUnspeccedGetPostThreadV2,
|
||||
type AppBskyUnspeccedDefs,
|
||||
type AppBskyUnspeccedGetPostThreadHiddenV2,
|
||||
type AppBskyUnspeccedGetPostThreadV2,
|
||||
type ModerationDecision,
|
||||
} from '@atproto/api'
|
||||
|
||||
export type ApiThreadItem =
|
||||
| AppBskyUnspeccedGetPostThreadV2.ThreadItem
|
||||
| AppBskyUnspeccedGetPostThreadHiddenV2.ThreadHiddenItem
|
||||
|
||||
export const postThreadQueryKeyRoot = 'getPostThreadV2' as const
|
||||
export const postThreadHiddenQueryKeyRoot = 'getPostThreadHiddenV2' as const
|
||||
|
||||
|
||||
@@ -2,12 +2,13 @@ import {
|
||||
type AppBskyFeedDefs,
|
||||
AppBskyFeedPost,
|
||||
AppBskyFeedThreadgate,
|
||||
AppBskyUnspeccedGetPostThreadV2,
|
||||
AppBskyUnspeccedDefs,
|
||||
type AppBskyUnspeccedGetPostThreadV2,
|
||||
AtUri,
|
||||
} from '@atproto/api'
|
||||
|
||||
import {
|
||||
type ApiThreadItem,
|
||||
type ThreadItem,
|
||||
type TraversalMetadata,
|
||||
} from '#/state/queries/usePostThread/types'
|
||||
@@ -47,9 +48,9 @@ export function getTraversalMetadata({
|
||||
nextItem,
|
||||
parentMetadata,
|
||||
}: {
|
||||
item: AppBskyUnspeccedDefs.ThreadItem
|
||||
prevItem?: AppBskyUnspeccedDefs.ThreadItem
|
||||
nextItem?: AppBskyUnspeccedDefs.ThreadItem
|
||||
item: ApiThreadItem
|
||||
prevItem?: ApiThreadItem
|
||||
nextItem?: ApiThreadItem
|
||||
parentMetadata?: TraversalMetadata
|
||||
}): TraversalMetadata {
|
||||
if (!AppBskyUnspeccedDefs.isThreadItemPost(item.value)) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
type AppBskyFeedDefs,
|
||||
type AppBskyFeedPost,
|
||||
type AppBskyUnspeccedDefs,
|
||||
type AppBskyUnspeccedGetPostThreadV2,
|
||||
AtUri,
|
||||
moderatePost,
|
||||
type ModerationOpts,
|
||||
@@ -10,6 +11,7 @@ import {
|
||||
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
import {
|
||||
type ApiThreadItem,
|
||||
type ThreadItem,
|
||||
type TraversalMetadata,
|
||||
} from '#/state/queries/usePostThread/types'
|
||||
@@ -18,10 +20,7 @@ export function threadPostNoUnauthenticated({
|
||||
uri,
|
||||
depth,
|
||||
value,
|
||||
}: AppBskyUnspeccedDefs.ThreadItem): Extract<
|
||||
ThreadItem,
|
||||
{type: 'threadPostNoUnauthenticated'}
|
||||
> {
|
||||
}: ApiThreadItem): Extract<ThreadItem, {type: 'threadPostNoUnauthenticated'}> {
|
||||
return {
|
||||
type: 'threadPostNoUnauthenticated',
|
||||
key: uri,
|
||||
@@ -35,10 +34,7 @@ export function threadPostNotFound({
|
||||
uri,
|
||||
depth,
|
||||
value,
|
||||
}: AppBskyUnspeccedDefs.ThreadItem): Extract<
|
||||
ThreadItem,
|
||||
{type: 'threadPostNotFound'}
|
||||
> {
|
||||
}: ApiThreadItem): Extract<ThreadItem, {type: 'threadPostNotFound'}> {
|
||||
return {
|
||||
type: 'threadPostNotFound',
|
||||
key: uri,
|
||||
@@ -52,10 +48,7 @@ export function threadPostBlocked({
|
||||
uri,
|
||||
depth,
|
||||
value,
|
||||
}: AppBskyUnspeccedDefs.ThreadItem): Extract<
|
||||
ThreadItem,
|
||||
{type: 'threadPostBlocked'}
|
||||
> {
|
||||
}: ApiThreadItem): Extract<ThreadItem, {type: 'threadPostBlocked'}> {
|
||||
return {
|
||||
type: 'threadPostBlocked',
|
||||
key: uri,
|
||||
@@ -125,12 +118,12 @@ export function readMore({
|
||||
export function postViewToThreadPlaceholder(
|
||||
post: AppBskyFeedDefs.PostView,
|
||||
): $Typed<
|
||||
Omit<AppBskyUnspeccedDefs.ThreadItem, 'value'> & {
|
||||
Omit<AppBskyUnspeccedGetPostThreadV2.ThreadItem, 'value'> & {
|
||||
value: $Typed<AppBskyUnspeccedDefs.ThreadItemPost>
|
||||
}
|
||||
> {
|
||||
return {
|
||||
$type: 'app.bsky.unspecced.defs#threadItem',
|
||||
$type: 'app.bsky.unspecced.getPostThreadV2#threadItem',
|
||||
uri: post.uri,
|
||||
depth: 0, // reset to 0 for highlighted post
|
||||
value: {
|
||||
|
||||
@@ -45,7 +45,7 @@ import {type ImagePickerAsset} from 'expo-image-picker'
|
||||
import {
|
||||
AppBskyFeedDefs,
|
||||
type AppBskyFeedGetPostThread,
|
||||
AppBskyUnspeccedGetPostThreadV2,
|
||||
AppBskyUnspeccedDefs,
|
||||
type BskyAgent,
|
||||
type RichText,
|
||||
} from '@atproto/api'
|
||||
@@ -427,7 +427,7 @@ export const ComposePost = ({
|
||||
}
|
||||
if (
|
||||
!res.data.thread.every(p =>
|
||||
AppBskyUnspeccedGetPostThreadV2.isThreadItemPost(p.value),
|
||||
AppBskyUnspeccedDefs.isThreadItemPost(p.value),
|
||||
)
|
||||
) {
|
||||
throw new Error(`composer: app view returned non-post items`)
|
||||
|
||||
@@ -326,8 +326,6 @@ export function PostThread({uri}: {uri: string | undefined}) {
|
||||
return arr
|
||||
}, [skeleton, deferParents, maxParents, maxReplies])
|
||||
|
||||
console.log({anchorIndex: posts.findIndex(p => p.ctx?.isHighlightedPost)})
|
||||
|
||||
// This is only used on the web to keep the post in view when its parents load.
|
||||
// On native, we rely on `maintainVisibleContentPosition` instead.
|
||||
const didAdjustScrollWeb = useRef<boolean>(false)
|
||||
@@ -423,8 +421,6 @@ export function PostThread({uri}: {uri: string | undefined}) {
|
||||
(skeleton.highlightedPost.ctx.isParentLoading ||
|
||||
Boolean(skeleton?.parents && skeleton.parents.length > 0))
|
||||
|
||||
console.log({hasParents})
|
||||
|
||||
const renderItem = ({item, index}: {item: RowItem; index: number}) => {
|
||||
if (item === REPLY_PROMPT && hasSession) {
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user