mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
at tooltip 170 lines 4.4 kB view raw
1import { 2 type AppBskyFeedDefs, 3 AppBskyFeedPost, 4 AppBskyFeedThreadgate, 5 AppBskyUnspeccedDefs, 6 type AppBskyUnspeccedGetPostThreadV2, 7 AtUri, 8} from '@atproto/api' 9 10import { 11 type ApiThreadItem, 12 type ThreadItem, 13 type TraversalMetadata, 14} from '#/state/queries/usePostThread/types' 15import {isDevMode} from '#/storage/hooks/dev-mode' 16import * as bsky from '#/types/bsky' 17 18export function getThreadgateRecord( 19 view: AppBskyUnspeccedGetPostThreadV2.OutputSchema['threadgate'], 20) { 21 return bsky.dangerousIsType<AppBskyFeedThreadgate.Record>( 22 view?.record, 23 AppBskyFeedThreadgate.isRecord, 24 ) 25 ? view?.record 26 : undefined 27} 28 29export function getRootPostAtUri(post: AppBskyFeedDefs.PostView) { 30 if ( 31 bsky.dangerousIsType<AppBskyFeedPost.Record>( 32 post.record, 33 AppBskyFeedPost.isRecord, 34 ) 35 ) { 36 if (post.record.reply?.root?.uri) { 37 return new AtUri(post.record.reply.root.uri) 38 } 39 } 40} 41 42export function getPostRecord(post: AppBskyFeedDefs.PostView) { 43 return post.record as AppBskyFeedPost.Record 44} 45 46export function getTraversalMetadata({ 47 item, 48 prevItem, 49 nextItem, 50 parentMetadata, 51}: { 52 item: ApiThreadItem 53 prevItem?: ApiThreadItem 54 nextItem?: ApiThreadItem 55 parentMetadata?: TraversalMetadata 56}): TraversalMetadata { 57 if (!AppBskyUnspeccedDefs.isThreadItemPost(item.value)) { 58 throw new Error(`Expected thread item to be a post`) 59 } 60 const repliesCount = item.value.post.replyCount || 0 61 const repliesUnhydrated = item.value.moreReplies || 0 62 const metadata = { 63 depth: item.depth, 64 /* 65 * Unknown until after traversal 66 */ 67 isLastChild: false, 68 /* 69 * Unknown until after traversal 70 */ 71 isLastSibling: false, 72 /* 73 * If it's a top level reply, bc we render each top-level branch as a 74 * separate tree, it's implicitly part of the last branch. For subsequent 75 * replies, we'll override this after traversal. 76 */ 77 isPartOfLastBranchFromDepth: item.depth === 1 ? 1 : undefined, 78 nextItemDepth: nextItem?.depth, 79 parentMetadata, 80 prevItemDepth: prevItem?.depth, 81 /* 82 * Unknown until after traversal 83 */ 84 precedesChildReadMore: false, 85 /* 86 * Unknown until after traversal 87 */ 88 followsReadMoreUp: false, 89 postData: { 90 uri: item.uri, 91 authorHandle: item.value.post.author.handle, 92 }, 93 repliesCount, 94 repliesUnhydrated, 95 repliesSeenCounter: 0, 96 repliesIndexCounter: 0, 97 replyIndex: 0, 98 skippedIndentIndices: new Set<number>(), 99 } 100 101 if (isDevMode()) { 102 // @ts-ignore dev only for debugging 103 metadata.postData.text = getPostRecord(item.value.post).text 104 } 105 106 return metadata 107} 108 109export function storeTraversalMetadata( 110 metadatas: Map<string, TraversalMetadata>, 111 metadata: TraversalMetadata, 112) { 113 metadatas.set(metadata.postData.uri, metadata) 114 115 if (isDevMode()) { 116 // @ts-ignore dev only for debugging 117 metadatas.set(metadata.postData.text, metadata) 118 // @ts-ignore 119 window.__thread = metadatas 120 } 121} 122 123export function getThreadPostUI({ 124 depth, 125 repliesCount, 126 prevItemDepth, 127 isLastChild, 128 skippedIndentIndices, 129 repliesSeenCounter, 130 repliesUnhydrated, 131 precedesChildReadMore, 132 followsReadMoreUp, 133}: TraversalMetadata): Extract<ThreadItem, {type: 'threadPost'}>['ui'] { 134 const isReplyAndHasReplies = 135 depth > 0 && 136 repliesCount > 0 && 137 (repliesCount - repliesUnhydrated === repliesSeenCounter || 138 repliesSeenCounter > 0) 139 return { 140 isAnchor: depth === 0, 141 showParentReplyLine: 142 followsReadMoreUp || 143 (!!prevItemDepth && prevItemDepth !== 0 && prevItemDepth < depth), 144 showChildReplyLine: depth < 0 || isReplyAndHasReplies, 145 indent: depth, 146 /* 147 * If there are no slices below this one, or the next slice has a depth <= 148 * than the depth of this post, it's the last child of the reply tree. It 149 * is not necessarily the last leaf in the parent branch, since it could 150 * have another sibling. 151 */ 152 isLastChild, 153 skippedIndentIndices, 154 precedesChildReadMore: precedesChildReadMore ?? false, 155 } 156} 157 158export function getThreadPostNoUnauthenticatedUI({ 159 depth, 160 prevItemDepth, 161}: { 162 depth: number 163 prevItemDepth?: number 164 nextItemDepth?: number 165}): Extract<ThreadItem, {type: 'threadPostNoUnauthenticated'}>['ui'] { 166 return { 167 showChildReplyLine: depth < 0, 168 showParentReplyLine: Boolean(prevItemDepth && prevItemDepth < depth), 169 } 170}