Something went wrong. Try again.
Bluesky app fork with some witchin' additions 💫
Something went wrong. Try again.
4.6 kB · 175 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176import { type AppBskyFeedDefs, AppBskyFeedPost, AppBskyFeedThreadgate, AppBskyUnspeccedDefs, type AppBskyUnspeccedGetPostThreadV2, AtUri,} from '@atproto/api'
import { type ApiThreadItem, type ThreadItem, type TraversalMetadata,} from '#/state/queries/usePostThread/types'import {isDevMode} from '#/storage/hooks/dev-mode'import * as bsky from '#/types/bsky'
export function getThreadgateRecord( view: AppBskyUnspeccedGetPostThreadV2.OutputSchema['threadgate'],) { return bsky.dangerousIsType<AppBskyFeedThreadgate.Record>( view?.record, AppBskyFeedThreadgate.isRecord, ) ? view?.record : undefined}
export function getRootPostAtUri(post: AppBskyFeedDefs.PostView) { if ( bsky.dangerousIsType<AppBskyFeedPost.Record>( post.record, AppBskyFeedPost.isRecord, ) ) { /** * If the record has no `reply` field, it is a root post. */ if (!post.record.reply) { return new AtUri(post.uri) } if (post.record.reply?.root?.uri) { return new AtUri(post.record.reply.root.uri) } }}
export function getPostRecord(post: AppBskyFeedDefs.PostView) { return post.record as AppBskyFeedPost.Record}
export function getTraversalMetadata({ item, prevItem, nextItem, parentMetadata,}: { item: ApiThreadItem prevItem?: ApiThreadItem nextItem?: ApiThreadItem parentMetadata?: TraversalMetadata}): TraversalMetadata { if (!AppBskyUnspeccedDefs.isThreadItemPost(item.value)) { throw new Error(`Expected thread item to be a post`) } const repliesCount = item.value.post.replyCount || 0 const repliesUnhydrated = item.value.moreReplies || 0 const metadata = { depth: item.depth, /* * Unknown until after traversal */ isLastChild: false, /* * Unknown until after traversal */ isLastSibling: false, /* * If it's a top level reply, bc we render each top-level branch as a * separate tree, it's implicitly part of the last branch. For subsequent * replies, we'll override this after traversal. */ isPartOfLastBranchFromDepth: item.depth === 1 ? 1 : undefined, nextItemDepth: nextItem?.depth, parentMetadata, prevItemDepth: prevItem?.depth, /* * Unknown until after traversal */ precedesChildReadMore: false, /* * Unknown until after traversal */ followsReadMoreUp: false, postData: { uri: item.uri, authorHandle: item.value.post.author.handle, }, repliesCount, repliesUnhydrated, repliesSeenCounter: 0, replyIndex: 0, skippedIndentIndices: new Set<number>(), }
if (isDevMode()) { // @ts-ignore dev only for debugging metadata.postData.text = getPostRecord(item.value.post).text }
return metadata}
export function storeTraversalMetadata( metadatas: Map<string, TraversalMetadata>, metadata: TraversalMetadata,) { metadatas.set(metadata.postData.uri, metadata)
if (isDevMode()) { // @ts-ignore dev only for debugging metadatas.set(metadata.postData.text, metadata) // @ts-ignore window.__thread = metadatas }}
export function getThreadPostUI({ depth, repliesCount, prevItemDepth, isLastChild, skippedIndentIndices, repliesSeenCounter, repliesUnhydrated, precedesChildReadMore, followsReadMoreUp,}: TraversalMetadata): Extract<ThreadItem, {type: 'threadPost'}>['ui'] { const isReplyAndHasReplies = depth > 0 && repliesCount > 0 && (repliesCount - repliesUnhydrated === repliesSeenCounter || repliesSeenCounter > 0) return { isAnchor: depth === 0, showParentReplyLine: followsReadMoreUp || (!!prevItemDepth && prevItemDepth !== 0 && prevItemDepth < depth), showChildReplyLine: depth < 0 || isReplyAndHasReplies, indent: depth, /* * If there are no slices below this one, or the next slice has a depth <= * than the depth of this post, it's the last child of the reply tree. It * is not necessarily the last leaf in the parent branch, since it could * have another sibling. */ isLastChild, skippedIndentIndices, precedesChildReadMore: precedesChildReadMore ?? false, }}
export function getThreadPostNoUnauthenticatedUI({ depth, prevItemDepth,}: { depth: number prevItemDepth?: number nextItemDepth?: number}): Extract<ThreadItem, {type: 'threadPostNoUnauthenticated'}>['ui'] { return { showChildReplyLine: depth < 0, showParentReplyLine: Boolean(prevItemDepth && prevItemDepth < depth), }}