about summary refs log tree commit diff
path: root/src/state/models/feeds/custom-feed.ts
diff options
context:
space:
mode:
authorJaz <ericvolp12@gmail.com>2023-05-30 18:25:29 -0700
committerGitHub <noreply@github.com>2023-05-30 18:25:29 -0700
commit09ade363fdcfadb03433385e0c5510bc58438a65 (patch)
tree710af28d1eb7f70acf81f86acb44759439e164fc /src/state/models/feeds/custom-feed.ts
parent7f76c2d67e62ba2d10e8b17673a7bbcf7248564f (diff)
parente224569a11b82361d782324a63bdfc19d44a3201 (diff)
downloadvoidsky-09ade363fdcfadb03433385e0c5510bc58438a65.tar.zst
Merge branch 'main' into inherit_system_theme
Diffstat (limited to 'src/state/models/feeds/custom-feed.ts')
-rw-r--r--src/state/models/feeds/custom-feed.ts120
1 files changed, 120 insertions, 0 deletions
diff --git a/src/state/models/feeds/custom-feed.ts b/src/state/models/feeds/custom-feed.ts
new file mode 100644
index 000000000..8fc1eb1ec
--- /dev/null
+++ b/src/state/models/feeds/custom-feed.ts
@@ -0,0 +1,120 @@
+import {AppBskyFeedDefs} from '@atproto/api'
+import {makeAutoObservable, runInAction} from 'mobx'
+import {RootStoreModel} from 'state/models/root-store'
+import {sanitizeDisplayName} from 'lib/strings/display-names'
+import {updateDataOptimistically} from 'lib/async/revertible'
+
+export class CustomFeedModel {
+  // data
+  _reactKey: string
+  data: AppBskyFeedDefs.GeneratorView
+  isOnline: boolean
+  isValid: boolean
+
+  constructor(
+    public rootStore: RootStoreModel,
+    view: AppBskyFeedDefs.GeneratorView,
+    isOnline?: boolean,
+    isValid?: boolean,
+  ) {
+    this._reactKey = view.uri
+    this.data = view
+    this.isOnline = isOnline ?? true
+    this.isValid = isValid ?? true
+    makeAutoObservable(
+      this,
+      {
+        rootStore: false,
+      },
+      {autoBind: true},
+    )
+  }
+
+  // local actions
+  // =
+
+  get uri() {
+    return this.data.uri
+  }
+
+  get displayName() {
+    if (this.data.displayName) {
+      return sanitizeDisplayName(this.data.displayName)
+    }
+    return `Feed by @${this.data.creator.handle}`
+  }
+
+  get isSaved() {
+    return this.rootStore.preferences.savedFeeds.includes(this.uri)
+  }
+
+  get isLiked() {
+    return this.data.viewer?.like
+  }
+
+  // public apis
+  // =
+
+  async save() {
+    await this.rootStore.preferences.addSavedFeed(this.uri)
+  }
+
+  async unsave() {
+    await this.rootStore.preferences.removeSavedFeed(this.uri)
+  }
+
+  async like() {
+    try {
+      await updateDataOptimistically(
+        this.data,
+        () => {
+          this.data.viewer = this.data.viewer || {}
+          this.data.viewer.like = 'pending'
+          this.data.likeCount = (this.data.likeCount || 0) + 1
+        },
+        () => this.rootStore.agent.like(this.data.uri, this.data.cid),
+        res => {
+          this.data.viewer = this.data.viewer || {}
+          this.data.viewer.like = res.uri
+        },
+      )
+    } catch (e: any) {
+      this.rootStore.log.error('Failed to like feed', e)
+    }
+  }
+
+  async unlike() {
+    if (!this.data.viewer?.like) {
+      return
+    }
+    try {
+      const likeUri = this.data.viewer.like
+      await updateDataOptimistically(
+        this.data,
+        () => {
+          this.data.viewer = this.data.viewer || {}
+          this.data.viewer.like = undefined
+          this.data.likeCount = (this.data.likeCount || 1) - 1
+        },
+        () => this.rootStore.agent.deleteLike(likeUri),
+      )
+    } catch (e: any) {
+      this.rootStore.log.error('Failed to unlike feed', e)
+    }
+  }
+
+  async reload() {
+    const res = await this.rootStore.agent.app.bsky.feed.getFeedGenerator({
+      feed: this.data.uri,
+    })
+    runInAction(() => {
+      this.data = res.data.view
+      this.isOnline = res.data.isOnline
+      this.isValid = res.data.isValid
+    })
+  }
+
+  serialize() {
+    return JSON.stringify(this.data)
+  }
+}