about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPaul Frazee <pfrazee@gmail.com>2023-04-24 17:02:58 -0500
committerGitHub <noreply@github.com>2023-04-24 17:02:58 -0500
commit1b356556c91ed07025927d4cb0be90a269396822 (patch)
tree667b9f39d3f484d2952cb7efc5892d20878a55d7
parentda8af38dcc23ea33c686714be2ce5f0bf0e65798 (diff)
downloadvoidsky-1b356556c91ed07025927d4cb0be90a269396822.tar.zst
Performance fixes with new getPosts (#525)
* Update notifications to fetch in a batch using getPosts

* Improve search perf with getPosts

* Bump @atproto/api@0.2.9

* Just use post uri for key
-rw-r--r--package.json2
-rw-r--r--src/state/models/content/post-thread.ts58
-rw-r--r--src/state/models/feeds/notifications.ts76
-rw-r--r--src/state/models/ui/search.ts23
-rw-r--r--src/view/com/search/SearchResults.tsx11
-rw-r--r--yarn.lock8
6 files changed, 97 insertions, 81 deletions
diff --git a/package.json b/package.json
index d6d29f3c9..0df1f464c 100644
--- a/package.json
+++ b/package.json
@@ -22,7 +22,7 @@
     "e2e:run": "detox test --configuration ios.sim.debug --take-screenshots all"
   },
   "dependencies": {
-    "@atproto/api": "0.2.8",
+    "@atproto/api": "0.2.9",
     "@bam.tech/react-native-image-resizer": "^3.0.4",
     "@braintree/sanitize-url": "^6.0.2",
     "@expo/webpack-config": "^18.0.1",
diff --git a/src/state/models/content/post-thread.ts b/src/state/models/content/post-thread.ts
index acc9bffa9..76cab5c61 100644
--- a/src/state/models/content/post-thread.ts
+++ b/src/state/models/content/post-thread.ts
@@ -11,13 +11,6 @@ import * as apilib from 'lib/api/index'
 import {cleanError} from 'lib/strings/errors'
 import {updateDataOptimistically} from 'lib/async/revertible'
 
-function* reactKeyGenerator(): Generator<string> {
-  let counter = 0
-  while (true) {
-    yield `item-${counter++}`
-  }
-}
-
 export class PostThreadItemModel {
   // ui state
   _reactKey: string = ''
@@ -55,10 +48,9 @@ export class PostThreadItemModel {
 
   constructor(
     public rootStore: RootStoreModel,
-    reactKey: string,
     v: AppBskyFeedDefs.ThreadViewPost,
   ) {
-    this._reactKey = reactKey
+    this._reactKey = `thread-${v.post.uri}`
     this.post = v.post
     if (FeedPost.isRecord(this.post.record)) {
       const valid = FeedPost.validateRecord(this.post.record)
@@ -82,7 +74,6 @@ export class PostThreadItemModel {
   }
 
   assignTreeModels(
-    keyGen: Generator<string>,
     v: AppBskyFeedDefs.ThreadViewPost,
     higlightedPostUri: string,
     includeParent = true,
@@ -91,22 +82,12 @@ export class PostThreadItemModel {
     // parents
     if (includeParent && v.parent) {
       if (AppBskyFeedDefs.isThreadViewPost(v.parent)) {
-        const parentModel = new PostThreadItemModel(
-          this.rootStore,
-          keyGen.next().value,
-          v.parent,
-        )
+        const parentModel = new PostThreadItemModel(this.rootStore, v.parent)
         parentModel._depth = this._depth - 1
         parentModel._showChildReplyLine = true
         if (v.parent.parent) {
           parentModel._showParentReplyLine = true //parentModel.uri !== higlightedPostUri
-          parentModel.assignTreeModels(
-            keyGen,
-            v.parent,
-            higlightedPostUri,
-            true,
-            false,
-          )
+          parentModel.assignTreeModels(v.parent, higlightedPostUri, true, false)
         }
         this.parent = parentModel
       } else if (AppBskyFeedDefs.isNotFoundPost(v.parent)) {
@@ -118,23 +99,13 @@ export class PostThreadItemModel {
       const replies = []
       for (const item of v.replies) {
         if (AppBskyFeedDefs.isThreadViewPost(item)) {
-          const itemModel = new PostThreadItemModel(
-            this.rootStore,
-            keyGen.next().value,
-            item,
-          )
+          const itemModel = new PostThreadItemModel(this.rootStore, item)
           itemModel._depth = this._depth + 1
           itemModel._showParentReplyLine =
             itemModel.parentUri !== higlightedPostUri
           if (item.replies?.length) {
             itemModel._showChildReplyLine = true
-            itemModel.assignTreeModels(
-              keyGen,
-              item,
-              higlightedPostUri,
-              false,
-              true,
-            )
+            itemModel.assignTreeModels(item, higlightedPostUri, false, true)
           }
           replies.push(itemModel)
         } else if (AppBskyFeedDefs.isNotFoundPost(item)) {
@@ -241,6 +212,19 @@ export class PostThreadModel {
     this.params = params
   }
 
+  static fromPostView(
+    rootStore: RootStoreModel,
+    postView: AppBskyFeedDefs.PostView,
+  ) {
+    const model = new PostThreadModel(rootStore, {uri: postView.uri})
+    model.resolvedUri = postView.uri
+    model.hasLoaded = true
+    model.thread = new PostThreadItemModel(rootStore, {
+      post: postView,
+    })
+    return model
+  }
+
   get hasContent() {
     return typeof this.thread !== 'undefined'
   }
@@ -360,6 +344,9 @@ export class PostThreadModel {
   }
 
   async _load(isRefreshing = false) {
+    if (this.hasLoaded && !isRefreshing) {
+      return
+    }
     this._xLoading(isRefreshing)
     try {
       const res = await this.rootStore.agent.getPostThread(
@@ -374,15 +361,12 @@ export class PostThreadModel {
 
   _replaceAll(res: GetPostThread.Response) {
     sortThread(res.data.thread)
-    const keyGen = reactKeyGenerator()
     const thread = new PostThreadItemModel(
       this.rootStore,
-      keyGen.next().value,
       res.data.thread as AppBskyFeedDefs.ThreadViewPost,
     )
     thread._isHighlightedPost = true
     thread.assignTreeModels(
-      keyGen,
       res.data.thread as AppBskyFeedDefs.ThreadViewPost,
       thread.uri,
     )
diff --git a/src/state/models/feeds/notifications.ts b/src/state/models/feeds/notifications.ts
index e2a18ea04..0bbbe215c 100644
--- a/src/state/models/feeds/notifications.ts
+++ b/src/state/models/feeds/notifications.ts
@@ -2,6 +2,7 @@ import {makeAutoObservable, runInAction} from 'mobx'
 import {
   AppBskyNotificationListNotifications as ListNotifications,
   AppBskyActorDefs,
+  AppBskyFeedDefs,
   AppBskyFeedPost,
   AppBskyFeedRepost,
   AppBskyFeedLike,
@@ -146,6 +147,14 @@ export class NotificationsFeedItemModel {
     return false
   }
 
+  get additionaDataUri(): string | undefined {
+    if (this.isReply || this.isQuote || this.isMention) {
+      return this.uri
+    } else if (this.isLike || this.isRepost) {
+      return this.subjectUri
+    }
+  }
+
   get subjectUri(): string {
     if (this.reasonSubject) {
       return this.reasonSubject
@@ -193,28 +202,11 @@ export class NotificationsFeedItemModel {
     )
   }
 
-  async fetchAdditionalData() {
-    if (!this.needsAdditionalData) {
-      return
-    }
-    let postUri
-    if (this.isReply || this.isQuote || this.isMention) {
-      postUri = this.uri
-    } else if (this.isLike || this.isRepost) {
-      postUri = this.subjectUri
-    }
-    if (postUri) {
-      this.additionalPost = new PostThreadModel(this.rootStore, {
-        uri: postUri,
-        depth: 0,
-      })
-      await this.additionalPost.setup().catch(e => {
-        this.rootStore.log.error(
-          'Failed to load post needed by notification',
-          e,
-        )
-      })
-    }
+  setAdditionalData(additionalPost: AppBskyFeedDefs.PostView) {
+    this.additionalPost = PostThreadModel.fromPostView(
+      this.rootStore,
+      additionalPost,
+    )
   }
 }
 
@@ -464,7 +456,13 @@ export class NotificationsFeedModel {
       'mostRecent',
       res.data.notifications[0],
     )
-    await notif.fetchAdditionalData()
+    const addedUri = notif.additionaDataUri
+    if (addedUri) {
+      const postsRes = await this.rootStore.agent.app.bsky.feed.getPosts({
+        uris: [addedUri],
+      })
+      notif.setAdditionalData(postsRes.data.posts[0])
+    }
     const filtered = this._filterNotifications([notif])
     return filtered[0]
   }
@@ -536,25 +534,39 @@ export class NotificationsFeedModel {
   async _fetchItemModels(
     items: ListNotifications.Notification[],
   ): Promise<NotificationsFeedItemModel[]> {
-    const promises = []
+    // construct item models and track who needs more data
     const itemModels: NotificationsFeedItemModel[] = []
+    const addedPostMap = new Map<string, NotificationsFeedItemModel[]>()
     for (const item of items) {
       const itemModel = new NotificationsFeedItemModel(
         this.rootStore,
         `item-${_idCounter++}`,
         item,
       )
-      if (itemModel.needsAdditionalData) {
-        promises.push(itemModel.fetchAdditionalData())
+      const uri = itemModel.additionaDataUri
+      if (uri) {
+        const models = addedPostMap.get(uri) || []
+        models.push(itemModel)
+        addedPostMap.set(uri, models)
       }
       itemModels.push(itemModel)
     }
-    await Promise.all(promises).catch(e => {
-      this.rootStore.log.error(
-        'Uncaught failure during notifications _processNotifications()',
-        e,
-      )
-    })
+
+    // fetch additional data
+    if (addedPostMap.size > 0) {
+      const postsRes = await this.rootStore.agent.app.bsky.feed.getPosts({
+        uris: Array.from(addedPostMap.keys()),
+      })
+      for (const post of postsRes.data.posts) {
+        const models = addedPostMap.get(post.uri)
+        if (models?.length) {
+          for (const model of models) {
+            model.setAdditionalData(post)
+          }
+        }
+      }
+    }
+
     return itemModels
   }
 
diff --git a/src/state/models/ui/search.ts b/src/state/models/ui/search.ts
index 330283a0b..4ab9db513 100644
--- a/src/state/models/ui/search.ts
+++ b/src/state/models/ui/search.ts
@@ -1,13 +1,14 @@
 import {makeAutoObservable, runInAction} from 'mobx'
 import {searchProfiles, searchPosts} from 'lib/api/search'
-import {AppBskyActorDefs} from '@atproto/api'
+import {PostThreadModel} from '../content/post-thread'
+import {AppBskyActorDefs, AppBskyFeedDefs} from '@atproto/api'
 import {RootStoreModel} from '../root-store'
 
 export class SearchUIModel {
   isPostsLoading = false
   isProfilesLoading = false
   query: string = ''
-  postUris: string[] = []
+  posts: PostThreadModel[] = []
   profiles: AppBskyActorDefs.ProfileView[] = []
 
   constructor(public rootStore: RootStoreModel) {
@@ -15,7 +16,7 @@ export class SearchUIModel {
   }
 
   async fetch(q: string) {
-    this.postUris = []
+    this.posts = []
     this.profiles = []
     this.query = q
     if (!q.trim()) {
@@ -29,8 +30,22 @@ export class SearchUIModel {
       searchPosts(q).catch(_e => []),
       searchProfiles(q).catch(_e => []),
     ])
+
+    let posts: AppBskyFeedDefs.PostView[] = []
+    if (postsSearch?.length) {
+      do {
+        const res = await this.rootStore.agent.app.bsky.feed.getPosts({
+          uris: postsSearch
+            .splice(0, 25)
+            .map(p => `at://${p.user.did}/${p.tid}`),
+        })
+        posts = posts.concat(res.data.posts)
+      } while (postsSearch.length)
+    }
     runInAction(() => {
-      this.postUris = postsSearch?.map(p => `at://${p.user.did}/${p.tid}`) || []
+      this.posts = posts.map(post =>
+        PostThreadModel.fromPostView(this.rootStore, post),
+      )
       this.isPostsLoading = false
     })
 
diff --git a/src/view/com/search/SearchResults.tsx b/src/view/com/search/SearchResults.tsx
index 5d6163d4b..3b05f75ea 100644
--- a/src/view/com/search/SearchResults.tsx
+++ b/src/view/com/search/SearchResults.tsx
@@ -49,7 +49,7 @@ const PostResults = observer(({model}: {model: SearchUIModel}) => {
     )
   }
 
-  if (model.postUris.length === 0) {
+  if (model.posts.length === 0) {
     return (
       <CenteredView>
         <Text type="xl" style={[styles.empty, pal.text]}>
@@ -61,8 +61,13 @@ const PostResults = observer(({model}: {model: SearchUIModel}) => {
 
   return (
     <ScrollView style={pal.view}>
-      {model.postUris.map(uri => (
-        <Post key={uri} uri={uri} hideError />
+      {model.posts.map(post => (
+        <Post
+          key={post.resolvedUri}
+          uri={post.resolvedUri}
+          initView={post}
+          hideError
+        />
       ))}
       <View style={s.footerSpacer} />
       <View style={s.footerSpacer} />
diff --git a/yarn.lock b/yarn.lock
index c5d29c2af..fb45b87a0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -30,10 +30,10 @@
     tlds "^1.234.0"
     typed-emitter "^2.1.0"
 
-"@atproto/api@0.2.8":
-  version "0.2.8"
-  resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.2.8.tgz#92ed413804ecb43aaa45ec18afc93d6f2b28a689"
-  integrity sha512-LfPgtf3UNg2W/AxHkJMJrLNT9QAD6bi16Sw5Zt3mgANrDnHWGygA7gRpeNdgVI+kFEhQfrIItemJvWLIB9BJDQ==
+"@atproto/api@0.2.9":
+  version "0.2.9"
+  resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.2.9.tgz#08e29da66d1a9001d9d3ce427548c1760d805e99"
+  integrity sha512-r00IqidX2YF3VUEa4MUO2Vxqp3+QhI1cSNcWgzT4LsANapzrwdDTM+rY2Ejp9na3F+unO4SWRW3o434cVmG5gw==
   dependencies:
     "@atproto/common-web" "*"
     "@atproto/uri" "*"