Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

remove resolution from post thread (#4297)

* remove resolution from post thread

nit

completely remove did cache lookup

move cache check for did to `usePostThreadQuery`

remove resolution from post thread

* helper function

* simplify

* simplify search too

* fix missing check for root or parent quoted post 🤯

* fix thread traversal

authored by

Hailey and committed by
GitHub
8d832342 21d4d5f6

+111 -64
+13 -5
src/state/queries/notifications/feed.ts
··· 17 17 */ 18 18 19 19 import {useEffect, useRef} from 'react' 20 - import {AppBskyActorDefs, AppBskyFeedDefs} from '@atproto/api' 20 + import {AppBskyActorDefs, AppBskyFeedDefs, AtUri} from '@atproto/api' 21 21 import { 22 22 InfiniteData, 23 23 QueryClient, ··· 30 30 import {useAgent} from '#/state/session' 31 31 import {useModerationOpts} from '../../preferences/moderation-opts' 32 32 import {STALE} from '..' 33 - import {embedViewRecordToPostView, getEmbeddedPost} from '../util' 33 + import { 34 + didOrHandleUriMatches, 35 + embedViewRecordToPostView, 36 + getEmbeddedPost, 37 + } from '../util' 34 38 import {FeedPage} from './types' 35 39 import {useUnreadNotificationsApi} from './unread' 36 40 import {fetchPage} from './util' ··· 142 146 queryClient: QueryClient, 143 147 uri: string, 144 148 ): Generator<AppBskyFeedDefs.PostView, void> { 149 + const atUri = new AtUri(uri) 150 + 145 151 const queryDatas = queryClient.getQueriesData<InfiniteData<FeedPage>>({ 146 152 queryKey: [RQKEY_ROOT], 147 153 }) ··· 149 155 if (!queryData?.pages) { 150 156 continue 151 157 } 158 + 152 159 for (const page of queryData?.pages) { 153 160 for (const item of page.items) { 154 - if (item.subject?.uri === uri) { 161 + if (item.subject && didOrHandleUriMatches(atUri, item.subject)) { 155 162 yield item.subject 156 163 } 164 + 157 165 const quotedPost = getEmbeddedPost(item.subject?.embed) 158 - if (quotedPost?.uri === uri) { 159 - yield embedViewRecordToPostView(quotedPost) 166 + if (quotedPost && didOrHandleUriMatches(atUri, quotedPost)) { 167 + yield embedViewRecordToPostView(quotedPost!) 160 168 } 161 169 } 162 170 }
+33 -13
src/state/queries/post-feed.ts
··· 35 35 import {useFeedTuners} from '../preferences/feed-tuners' 36 36 import {useModerationOpts} from '../preferences/moderation-opts' 37 37 import {usePreferencesQuery} from './preferences' 38 - import {embedViewRecordToPostView, getEmbeddedPost} from './util' 38 + import { 39 + didOrHandleUriMatches, 40 + embedViewRecordToPostView, 41 + getEmbeddedPost, 42 + } from './util' 39 43 40 44 type ActorDid = string 41 45 type AuthorFilter = ··· 448 452 queryClient: QueryClient, 449 453 uri: string, 450 454 ): Generator<AppBskyFeedDefs.PostView, undefined> { 455 + const atUri = new AtUri(uri) 456 + 451 457 const queryDatas = queryClient.getQueriesData< 452 458 InfiniteData<FeedPageUnselected> 453 459 >({ ··· 459 465 } 460 466 for (const page of queryData?.pages) { 461 467 for (const item of page.feed) { 462 - if (item.post.uri === uri) { 468 + if (didOrHandleUriMatches(atUri, item.post)) { 463 469 yield item.post 464 470 } 471 + 465 472 const quotedPost = getEmbeddedPost(item.post.embed) 466 - if (quotedPost?.uri === uri) { 473 + if (quotedPost && didOrHandleUriMatches(atUri, quotedPost)) { 467 474 yield embedViewRecordToPostView(quotedPost) 468 475 } 469 - if ( 470 - AppBskyFeedDefs.isPostView(item.reply?.parent) && 471 - item.reply?.parent?.uri === uri 472 - ) { 473 - yield item.reply.parent 476 + 477 + if (AppBskyFeedDefs.isPostView(item.reply?.parent)) { 478 + if (didOrHandleUriMatches(atUri, item.reply.parent)) { 479 + yield item.reply.parent 480 + } 481 + 482 + const parentQuotedPost = getEmbeddedPost(item.reply.parent.embed) 483 + if ( 484 + parentQuotedPost && 485 + didOrHandleUriMatches(atUri, parentQuotedPost) 486 + ) { 487 + yield embedViewRecordToPostView(parentQuotedPost) 488 + } 474 489 } 475 - if ( 476 - AppBskyFeedDefs.isPostView(item.reply?.root) && 477 - item.reply?.root?.uri === uri 478 - ) { 479 - yield item.reply.root 490 + 491 + if (AppBskyFeedDefs.isPostView(item.reply?.root)) { 492 + if (didOrHandleUriMatches(atUri, item.reply.root)) { 493 + yield item.reply.root 494 + } 495 + 496 + const rootQuotedPost = getEmbeddedPost(item.reply.root.embed) 497 + if (rootQuotedPost && didOrHandleUriMatches(atUri, rootQuotedPost)) { 498 + yield embedViewRecordToPostView(rootQuotedPost) 499 + } 480 500 } 481 501 } 482 502 }
+14 -11
src/state/queries/post-thread.ts
··· 4 4 AppBskyFeedDefs, 5 5 AppBskyFeedGetPostThread, 6 6 AppBskyFeedPost, 7 + AtUri, 7 8 ModerationDecision, 8 9 ModerationOpts, 9 10 } from '@atproto/api' ··· 24 25 findAllPostsInQueryData as findAllPostsInFeedQueryData, 25 26 findAllProfilesInQueryData as findAllProfilesInFeedQueryData, 26 27 } from './post-feed' 27 - import {embedViewRecordToPostView, getEmbeddedPost} from './util' 28 + import { 29 + didOrHandleUriMatches, 30 + embedViewRecordToPostView, 31 + getEmbeddedPost, 32 + } from './util' 28 33 29 34 const RQKEY_ROOT = 'post-thread' 30 35 export const RQKEY = (uri: string) => [RQKEY_ROOT, uri] ··· 91 96 }, 92 97 enabled: !!uri, 93 98 placeholderData: () => { 94 - if (!uri) { 95 - return undefined 96 - } 97 - { 98 - const post = findPostInQueryData(queryClient, uri) 99 - if (post) { 100 - return post 101 - } 99 + if (!uri) return 100 + const post = findPostInQueryData(queryClient, uri) 101 + if (post) { 102 + return post 102 103 } 103 104 return undefined 104 105 }, ··· 271 272 queryClient: QueryClient, 272 273 uri: string, 273 274 ): Generator<ThreadNode, void> { 275 + const atUri = new AtUri(uri) 276 + 274 277 const queryDatas = queryClient.getQueriesData<ThreadNode>({ 275 278 queryKey: [RQKEY_ROOT], 276 279 }) ··· 279 282 continue 280 283 } 281 284 for (const item of traverseThread(queryData)) { 282 - if (item.uri === uri) { 285 + if (item.type === 'post' && didOrHandleUriMatches(atUri, item.post)) { 283 286 const placeholder = threadNodeToPlaceholderThread(item) 284 287 if (placeholder) { 285 288 yield placeholder ··· 287 290 } 288 291 const quotedPost = 289 292 item.type === 'post' ? getEmbeddedPost(item.post.embed) : undefined 290 - if (quotedPost?.uri === uri) { 293 + if (quotedPost && didOrHandleUriMatches(atUri, quotedPost)) { 291 294 yield embedViewRecordToPlaceholderThread(quotedPost) 292 295 } 293 296 }
+11 -3
src/state/queries/search-posts.ts
··· 2 2 AppBskyActorDefs, 3 3 AppBskyFeedDefs, 4 4 AppBskyFeedSearchPosts, 5 + AtUri, 5 6 } from '@atproto/api' 6 7 import { 7 8 InfiniteData, ··· 11 12 } from '@tanstack/react-query' 12 13 13 14 import {useAgent} from '#/state/session' 14 - import {embedViewRecordToPostView, getEmbeddedPost} from './util' 15 + import { 16 + didOrHandleUriMatches, 17 + embedViewRecordToPostView, 18 + getEmbeddedPost, 19 + } from './util' 15 20 16 21 const searchPostsQueryKeyRoot = 'search-posts' 17 22 const searchPostsQueryKey = ({query, sort}: {query: string; sort?: string}) => [ ··· 62 67 >({ 63 68 queryKey: [searchPostsQueryKeyRoot], 64 69 }) 70 + const atUri = new AtUri(uri) 71 + 65 72 for (const [_queryKey, queryData] of queryDatas) { 66 73 if (!queryData?.pages) { 67 74 continue 68 75 } 69 76 for (const page of queryData?.pages) { 70 77 for (const post of page.posts) { 71 - if (post.uri === uri) { 78 + if (didOrHandleUriMatches(atUri, post)) { 72 79 yield post 73 80 } 81 + 74 82 const quotedPost = getEmbeddedPost(post.embed) 75 - if (quotedPost?.uri === uri) { 83 + if (quotedPost && didOrHandleUriMatches(atUri, quotedPost)) { 76 84 yield embedViewRecordToPostView(quotedPost) 77 85 } 78 86 }
+19
src/state/queries/util.ts
··· 1 1 import { 2 + AppBskyActorDefs, 2 3 AppBskyEmbedRecord, 3 4 AppBskyEmbedRecordWithMedia, 4 5 AppBskyFeedDefs, 5 6 AppBskyFeedPost, 7 + AtUri, 6 8 } from '@atproto/api' 7 9 import {InfiniteData, QueryClient, QueryKey} from '@tanstack/react-query' 8 10 ··· 20 22 return data 21 23 }) 22 24 queryClient.invalidateQueries({queryKey}) 25 + } 26 + 27 + // Given an AtUri, this function will check if the AtUri matches a 28 + // hit regardless of whether the AtUri uses a DID or handle as a host. 29 + // 30 + // AtUri should be the URI that is being searched for, while currentUri 31 + // is the URI that is being checked. currentAuthor is the author 32 + // of the currentUri that is being checked. 33 + export function didOrHandleUriMatches( 34 + atUri: AtUri, 35 + record: {uri: string; author: AppBskyActorDefs.ProfileViewBasic}, 36 + ) { 37 + if (atUri.host.startsWith('did:')) { 38 + return atUri.href === record.uri 39 + } 40 + 41 + return atUri.host === record.author.handle && record.uri.endsWith(atUri.rkey) 23 42 } 24 43 25 44 export function getEmbeddedPost(
+21 -32
src/view/screens/PostThread.tsx
··· 1 1 import React from 'react' 2 2 import {StyleSheet, View} from 'react-native' 3 3 import Animated from 'react-native-reanimated' 4 + import {useSafeAreaInsets} from 'react-native-safe-area-context' 4 5 import {useFocusEffect} from '@react-navigation/native' 5 6 import {useQueryClient} from '@tanstack/react-query' 6 - import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' 7 - import {makeRecordUri} from 'lib/strings/url-helpers' 8 - import {PostThread as PostThreadComponent} from '../com/post-thread/PostThread' 9 - import {ComposePrompt} from 'view/com/composer/Prompt' 10 - import {s} from 'lib/styles' 11 - import {useSafeAreaInsets} from 'react-native-safe-area-context' 7 + import {clamp} from 'lodash' 8 + 9 + import {isWeb} from '#/platform/detection' 12 10 import { 13 11 RQKEY as POST_THREAD_RQKEY, 14 12 ThreadNode, 15 13 } from '#/state/queries/post-thread' 16 - import {clamp} from 'lodash' 17 - import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' 18 - import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode' 14 + import {useSession} from '#/state/session' 19 15 import {useSetMinimalShellMode} from '#/state/shell' 20 - import {useResolveUriQuery} from '#/state/queries/resolve-uri' 21 - import {ErrorMessage} from '../com/util/error/ErrorMessage' 22 - import {CenteredView} from '../com/util/Views' 23 16 import {useComposerControls} from '#/state/shell/composer' 24 - import {useSession} from '#/state/session' 25 - import {isWeb} from '#/platform/detection' 17 + import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode' 18 + import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' 19 + import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' 20 + import {makeRecordUri} from 'lib/strings/url-helpers' 21 + import {s} from 'lib/styles' 22 + import {ComposePrompt} from 'view/com/composer/Prompt' 23 + import {PostThread as PostThreadComponent} from '../com/post-thread/PostThread' 26 24 27 25 type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostThread'> 28 26 export function PostThreadScreen({route}: Props) { ··· 35 33 const {name, rkey} = route.params 36 34 const {isMobile} = useWebMediaQueries() 37 35 const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey) 38 - const {data: resolvedUri, error: uriError} = useResolveUriQuery(uri) 39 36 const [canReply, setCanReply] = React.useState(false) 40 37 41 38 useFocusEffect( ··· 45 42 ) 46 43 47 44 const onPressReply = React.useCallback(() => { 48 - if (!resolvedUri) { 45 + if (!uri) { 49 46 return 50 47 } 51 - const thread = queryClient.getQueryData<ThreadNode>( 52 - POST_THREAD_RQKEY(resolvedUri.uri), 53 - ) 48 + const thread = queryClient.getQueryData<ThreadNode>(POST_THREAD_RQKEY(uri)) 54 49 if (thread?.type !== 'post') { 55 50 return 56 51 } ··· 64 59 }, 65 60 onPost: () => 66 61 queryClient.invalidateQueries({ 67 - queryKey: POST_THREAD_RQKEY(resolvedUri.uri || ''), 62 + queryKey: POST_THREAD_RQKEY(uri), 68 63 }), 69 64 }) 70 - }, [openComposer, queryClient, resolvedUri]) 65 + }, [openComposer, queryClient, uri]) 71 66 72 67 return ( 73 68 <View style={s.hContentRegion}> 74 69 <View style={s.flex1}> 75 - {uriError ? ( 76 - <CenteredView> 77 - <ErrorMessage message={String(uriError)} /> 78 - </CenteredView> 79 - ) : ( 80 - <PostThreadComponent 81 - uri={resolvedUri?.uri} 82 - onPressReply={onPressReply} 83 - onCanReply={setCanReply} 84 - /> 85 - )} 70 + <PostThreadComponent 71 + uri={uri} 72 + onPressReply={onPressReply} 73 + onCanReply={setCanReply} 74 + /> 86 75 </View> 87 76 {isMobile && canReply && hasSession && ( 88 77 <Animated.View