about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/lib/api/feed-manip.ts25
-rw-r--r--src/state/models/feed-view.ts89
-rw-r--r--src/view/com/composer/Composer.tsx2
-rw-r--r--src/view/screens/Home.tsx2
-rw-r--r--src/view/screens/Notifications.tsx3
5 files changed, 49 insertions, 72 deletions
diff --git a/src/lib/api/feed-manip.ts b/src/lib/api/feed-manip.ts
index a54b7d25d..e9a32b7a6 100644
--- a/src/lib/api/feed-manip.ts
+++ b/src/lib/api/feed-manip.ts
@@ -10,10 +10,12 @@ export type FeedTunerFn = (
 ) => void
 
 export class FeedViewPostsSlice {
+  isFlattenedReply = false
+
   constructor(public items: FeedViewPost[] = []) {}
 
   get uri() {
-    if (this.isReply) {
+    if (this.isFlattenedReply) {
       return this.items[1].post.uri
     }
     return this.items[0].post.uri
@@ -39,12 +41,8 @@ export class FeedViewPostsSlice {
     return this.isThread && !this.items[0].reply
   }
 
-  get isReply() {
-    return this.items.length > 1 && !this.isThread
-  }
-
   get rootItem() {
-    if (this.isReply) {
+    if (this.isFlattenedReply) {
       return this.items[1]
     }
     return this.items[0]
@@ -70,6 +68,7 @@ export class FeedViewPostsSlice {
 
   flattenReplyParent() {
     if (this.items[0].reply?.parent) {
+      this.isFlattenedReply = true
       this.items.splice(0, 0, {post: this.items[0].reply?.parent})
     }
   }
@@ -105,6 +104,11 @@ export class FeedTuner {
       slices.unshift(new FeedViewPostsSlice([item]))
     }
 
+    // run the custom tuners
+    for (const tunerFn of tunerFns) {
+      tunerFn(this, slices)
+    }
+
     // remove any items already "seen"
     const soonToBeSeenUris: Set<string> = new Set()
     for (let i = slices.length - 1; i >= 0; i--) {
@@ -135,11 +139,6 @@ export class FeedTuner {
     // sort by slice roots' timestamps
     slices.sort((a, b) => b.ts.localeCompare(a.ts))
 
-    // run the custom tuners
-    for (const tunerFn of tunerFns) {
-      tunerFn(this, slices)
-    }
-
     for (const slice of slices) {
       for (const item of slice.items) {
         this.seenUris.add(item.post.uri)
@@ -170,12 +169,12 @@ export class FeedTuner {
   static likedRepliesOnly(tuner: FeedTuner, slices: FeedViewPostsSlice[]) {
     // remove any replies without at least 2 likes
     for (let i = slices.length - 1; i >= 0; i--) {
-      if (slices[i].isFullThread) {
+      if (slices[i].isFullThread || !slices[i].rootItem.reply) {
         continue
       }
       const item = slices[i].rootItem
       const isRepost = Boolean(item.reason)
-      if (item.reply && !isRepost && item.post.upvoteCount < 2) {
+      if (!isRepost && item.post.upvoteCount < 2) {
         slices.splice(i, 1)
       }
     }
diff --git a/src/state/models/feed-view.ts b/src/state/models/feed-view.ts
index 954c19a8d..083863fe2 100644
--- a/src/state/models/feed-view.ts
+++ b/src/state/models/feed-view.ts
@@ -254,6 +254,7 @@ export class FeedModel {
 
   // data
   slices: FeedSliceModel[] = []
+  nextSlices: FeedSliceModel[] = []
 
   constructor(
     public rootStore: RootStoreModel,
@@ -325,6 +326,7 @@ export class FeedModel {
     this.loadMoreCursor = undefined
     this.pollCursor = undefined
     this.slices = []
+    this.nextSlices = []
     this.tuner.reset()
   }
 
@@ -423,30 +425,6 @@ export class FeedModel {
   })
 
   /**
-   * Load more posts to the start of the feed
-   */
-  loadLatest = bundleAsync(async () => {
-    await this.lock.acquireAsync()
-    try {
-      this.setHasNewLatest(false)
-      this._xLoading()
-      try {
-        const res = await this._getFeed({limit: PAGE_SIZE})
-        await this._prependAll(res)
-        this._xIdle()
-      } catch (e: any) {
-        this._xIdle() // don't bubble the error to the user
-        this.rootStore.log.error('FeedView: Failed to load latest', {
-          params: this.params,
-          e,
-        })
-      }
-    } finally {
-      this.lock.release()
-    }
-  })
-
-  /**
    * Update content in-place
    */
   update = bundleAsync(async () => {
@@ -487,22 +465,42 @@ export class FeedModel {
   /**
    * Check if new posts are available
    */
-  async checkForLatest() {
+  async checkForLatest({autoPrepend}: {autoPrepend?: boolean} = {}) {
     if (this.hasNewLatest || this.feedType === 'suggested') {
       return
     }
-    const res = await this._getFeed({limit: 1})
-    const currentLatestUri = this.pollCursor
-    const item = res.data.feed?.[0]
-    if (!item) {
-      return
-    }
-    if (AppBskyFeedFeedViewPost.isReasonRepost(item.reason)) {
-      if (item.reason.by.did === this.rootStore.me.did) {
-        return // ignore reposts by the user
+    const res = await this._getFeed({limit: PAGE_SIZE})
+    const tuner = new FeedTuner()
+    const nextSlices = tuner.tune(res.data.feed, this.feedTuners)
+    if (nextSlices[0]?.uri !== this.slices[0]?.uri) {
+      const nextSlicesModels = nextSlices.map(
+        slice =>
+          new FeedSliceModel(this.rootStore, `item-${_idCounter++}`, slice),
+      )
+      if (autoPrepend) {
+        this.slices = nextSlicesModels.concat(
+          this.slices.filter(slice1 =>
+            nextSlicesModels.find(slice2 => slice1.uri === slice2.uri),
+          ),
+        )
+        this.setHasNewLatest(false)
+      } else {
+        this.nextSlices = nextSlicesModels
+        this.setHasNewLatest(true)
       }
+    } else {
+      this.setHasNewLatest(false)
+    }
+  }
+
+  /**
+   * Sets the current slices to the "next slices" loaded by checkForLatest
+   */
+  resetToLatest() {
+    if (this.nextSlices.length) {
+      this.slices = this.nextSlices
     }
-    this.setHasNewLatest(item.post.uri !== currentLatestUri)
+    this.setHasNewLatest(false)
   }
 
   /**
@@ -574,27 +572,6 @@ export class FeedModel {
     })
   }
 
-  private async _prependAll(
-    res: GetTimeline.Response | GetAuthorFeed.Response,
-  ) {
-    this.pollCursor = res.data.feed[0]?.post.uri
-
-    const slices = this.tuner.tune(res.data.feed, this.feedTuners)
-
-    const toPrepend: FeedSliceModel[] = []
-    for (const slice of slices) {
-      const itemModel = new FeedSliceModel(
-        this.rootStore,
-        `item-${_idCounter++}`,
-        slice,
-      )
-      toPrepend.push(itemModel)
-    }
-    runInAction(() => {
-      this.slices = toPrepend.concat(this.slices)
-    })
-  }
-
   private _updateAll(res: GetTimeline.Response | GetAuthorFeed.Response) {
     for (const item of res.data.feed) {
       const existingSlice = this.slices.find(slice =>
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx
index e10e801e4..572eea927 100644
--- a/src/view/com/composer/Composer.tsx
+++ b/src/view/com/composer/Composer.tsx
@@ -166,7 +166,7 @@ export const ComposePost = observer(function ComposePost({
       setIsProcessing(false)
       return
     }
-    store.me.mainFeed.loadLatest()
+    store.me.mainFeed.checkForLatest({autoPrepend: true})
     onPost?.()
     hackfixOnClose()
     Toast.show(`Your ${replyTo ? 'reply' : 'post'} has been published`)
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
index 505f9e723..4f2bc4c15 100644
--- a/src/view/screens/Home.tsx
+++ b/src/view/screens/Home.tsx
@@ -158,7 +158,7 @@ const FeedPage = observer(
     }, [feed])
 
     const onPressLoadLatest = React.useCallback(() => {
-      feed.refresh()
+      feed.resetToLatest()
       scrollToTop()
     }, [feed, scrollToTop])
 
diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx
index b704f9c45..7da563843 100644
--- a/src/view/screens/Notifications.tsx
+++ b/src/view/screens/Notifications.tsx
@@ -74,7 +74,8 @@ export const NotificationsScreen = withAuthRequired(
       React.useCallback(() => {
         store.log.debug('NotificationsScreen: Updating feed')
         const softResetSub = store.onScreenSoftReset(scrollToTop)
-        store.me.notifications.update()
+        store.me.notifications.loadUnreadCount()
+        store.me.notifications.loadLatest()
         screen('Notifications')
 
         return () => {