about summary refs log tree commit diff
path: root/src/state/models/feed-view.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/state/models/feed-view.ts')
-rw-r--r--src/state/models/feed-view.ts121
1 files changed, 101 insertions, 20 deletions
diff --git a/src/state/models/feed-view.ts b/src/state/models/feed-view.ts
index 1fc507a70..4b915a766 100644
--- a/src/state/models/feed-view.ts
+++ b/src/state/models/feed-view.ts
@@ -25,15 +25,22 @@ export class FeedViewItemModel implements bsky.FeedView.FeedItem {
 }
 
 export class FeedViewModel implements bsky.FeedView.Response {
-  state = 'idle'
+  isLoading = false
+  isRefreshing = false
+  hasLoaded = false
   error = ''
   params: bsky.FeedView.Params
   feed: FeedViewItemModel[] = []
+  _loadMorePromise: Promise<void> | undefined
 
   constructor(public rootStore: RootStoreModel, params: bsky.FeedView.Params) {
     makeAutoObservable(
       this,
-      {rootStore: false, params: false},
+      {
+        rootStore: false,
+        params: false,
+        _loadMorePromise: false,
+      },
       {autoBind: true},
     )
     this.params = params
@@ -47,52 +54,126 @@ export class FeedViewModel implements bsky.FeedView.Response {
     return this.error !== ''
   }
 
-  get isLoading() {
-    return this.state === 'loading'
+  get isEmpty() {
+    return this.hasLoaded && !this.hasContent
   }
 
-  get isEmpty() {
-    return !this.hasContent && !this.hasError && !this.isLoading
+  get loadMoreCursor() {
+    if (this.hasContent) {
+      return this.feed[this.feed.length - 1].indexedAt
+    }
+    return undefined
   }
 
-  async fetch() {
+  // public api
+  // =
+
+  async setup() {
+    if (this._loadMorePromise) {
+      return this._loadMorePromise
+    }
     if (this.hasContent) {
-      await this.updateContent()
+      await this._refresh()
     } else {
-      await this.initialLoad()
+      await this._initialLoad()
+    }
+  }
+
+  async refresh() {
+    if (this._loadMorePromise) {
+      return this._loadMorePromise
     }
+    await this._refresh()
   }
 
-  async initialLoad() {
-    this.state = 'loading'
+  async loadMore() {
+    if (this._loadMorePromise) {
+      return this._loadMorePromise
+    }
+    this._loadMorePromise = this._loadMore()
+    await this._loadMorePromise
+    this._loadMorePromise = undefined
+  }
+
+  // state transitions
+  // =
+
+  private _xLoading(isRefreshing = false) {
+    this.isLoading = true
+    this.isRefreshing = isRefreshing
     this.error = ''
+  }
+
+  private _xIdle(err: string = '') {
+    this.isLoading = false
+    this.isRefreshing = false
+    this.hasLoaded = true
+    this.error = err
+  }
+
+  // loader functions
+  // =
+
+  private async _initialLoad() {
+    console.log('_initialLoad()')
+    this._xLoading()
+    await new Promise(r => setTimeout(r, 1e3)) // DEBUG
     try {
       const res = (await this.rootStore.api.mainPds.view(
         'blueskyweb.xyz:FeedView',
         this.params,
       )) as bsky.FeedView.Response
       this._replaceAll(res)
-      runInAction(() => {
-        this.state = 'idle'
-      })
+      this._xIdle()
     } catch (e: any) {
-      runInAction(() => {
-        this.state = 'error'
-        this.error = `Failed to load feed: ${e.toString()}`
+      this._xIdle(`Failed to load feed: ${e.toString()}`)
+    }
+  }
+
+  private async _loadMore() {
+    console.log('_loadMore()')
+    this._xLoading()
+    await new Promise(r => setTimeout(r, 1e3)) // DEBUG
+    try {
+      const params = Object.assign({}, this.params, {
+        before: this.loadMoreCursor,
       })
+      const res = (await this.rootStore.api.mainPds.view(
+        'blueskyweb.xyz:FeedView',
+        params,
+      )) as bsky.FeedView.Response
+      this._appendAll(res)
+      this._xIdle()
+    } catch (e: any) {
+      this._xIdle(`Failed to load feed: ${e.toString()}`)
     }
   }
 
-  async updateContent() {
+  private async _refresh() {
+    console.log('_refresh()')
+    this._xLoading(true)
     // TODO: refetch and update items
+    await new Promise(r => setTimeout(r, 1e3)) // DEBUG
+    this._xIdle()
   }
 
   private _replaceAll(res: bsky.FeedView.Response) {
     this.feed.length = 0
     let counter = 0
     for (const item of res.feed) {
-      // TODO: validate .record
-      this.feed.push(new FeedViewItemModel(`item-${counter++}`, item))
+      this._append(counter++, item)
+    }
+  }
+
+  private _appendAll(res: bsky.FeedView.Response) {
+    let counter = this.feed.length
+    for (const item of res.feed) {
+      this._append(counter++, item)
     }
   }
+
+  private _append(keyId: number, item: bsky.FeedView.FeedItem) {
+    // TODO: validate .record
+    this.feed.push(new FeedViewItemModel(`item-${keyId}`, item))
+  }
 }