about summary refs log tree commit diff
path: root/src/state
diff options
context:
space:
mode:
Diffstat (limited to 'src/state')
-rw-r--r--src/state/models/content/post-thread.ts22
-rw-r--r--src/state/models/content/post.ts122
-rw-r--r--src/state/models/content/profile.ts18
-rw-r--r--src/state/models/discovery/suggested-posts.ts88
-rw-r--r--src/state/models/feeds/notifications.ts54
-rw-r--r--src/state/models/feeds/posts.ts22
6 files changed, 106 insertions, 220 deletions
diff --git a/src/state/models/content/post-thread.ts b/src/state/models/content/post-thread.ts
index 76cab5c61..8f9a55032 100644
--- a/src/state/models/content/post-thread.ts
+++ b/src/state/models/content/post-thread.ts
@@ -10,6 +10,13 @@ import {RootStoreModel} from '../root-store'
 import * as apilib from 'lib/api/index'
 import {cleanError} from 'lib/strings/errors'
 import {updateDataOptimistically} from 'lib/async/revertible'
+import {PostLabelInfo, PostModeration} from 'lib/labeling/types'
+import {
+  getEmbedLabels,
+  filterAccountLabels,
+  filterProfileLabels,
+  getPostModeration,
+} from 'lib/labeling/helpers'
 
 export class PostThreadItemModel {
   // ui state
@@ -46,6 +53,21 @@ export class PostThreadItemModel {
     return this.rootStore.mutedThreads.uris.has(this.rootUri)
   }
 
+  get labelInfo(): PostLabelInfo {
+    return {
+      postLabels: (this.post.labels || []).concat(
+        getEmbedLabels(this.post.embed),
+      ),
+      accountLabels: filterAccountLabels(this.post.author.labels),
+      profileLabels: filterProfileLabels(this.post.author.labels),
+      isMuted: this.post.author.viewer?.muted || false,
+    }
+  }
+
+  get moderation(): PostModeration {
+    return getPostModeration(this.rootStore, this.labelInfo)
+  }
+
   constructor(
     public rootStore: RootStoreModel,
     v: AppBskyFeedDefs.ThreadViewPost,
diff --git a/src/state/models/content/post.ts b/src/state/models/content/post.ts
deleted file mode 100644
index 7ba633366..000000000
--- a/src/state/models/content/post.ts
+++ /dev/null
@@ -1,122 +0,0 @@
-import {makeAutoObservable} from 'mobx'
-import {AppBskyFeedPost as Post} from '@atproto/api'
-import {AtUri} from '@atproto/api'
-import {RootStoreModel} from '../root-store'
-import {cleanError} from 'lib/strings/errors'
-
-type RemoveIndex<T> = {
-  [P in keyof T as string extends P
-    ? never
-    : number extends P
-    ? never
-    : P]: T[P]
-}
-export class PostModel implements RemoveIndex<Post.Record> {
-  // state
-  isLoading = false
-  hasLoaded = false
-  error = ''
-  uri: string = ''
-
-  // data
-  text: string = ''
-  entities?: Post.Entity[]
-  reply?: Post.ReplyRef
-  createdAt: string = ''
-
-  constructor(public rootStore: RootStoreModel, uri: string) {
-    makeAutoObservable(
-      this,
-      {
-        rootStore: false,
-        uri: false,
-      },
-      {autoBind: true},
-    )
-    this.uri = uri
-  }
-
-  get hasContent() {
-    return this.createdAt !== ''
-  }
-
-  get hasError() {
-    return this.error !== ''
-  }
-
-  get isEmpty() {
-    return this.hasLoaded && !this.hasContent
-  }
-
-  get rootUri(): string {
-    if (this.reply?.root.uri) {
-      return this.reply.root.uri
-    }
-    return this.uri
-  }
-
-  get isThreadMuted() {
-    return this.rootStore.mutedThreads.uris.has(this.rootUri)
-  }
-
-  // public api
-  // =
-
-  async setup() {
-    await this._load()
-  }
-
-  async toggleThreadMute() {
-    if (this.isThreadMuted) {
-      this.rootStore.mutedThreads.uris.delete(this.rootUri)
-    } else {
-      this.rootStore.mutedThreads.uris.add(this.rootUri)
-    }
-  }
-
-  // state transitions
-  // =
-
-  _xLoading() {
-    this.isLoading = true
-    this.error = ''
-  }
-
-  _xIdle(err?: any) {
-    this.isLoading = false
-    this.hasLoaded = true
-    this.error = cleanError(err)
-    if (err) {
-      this.rootStore.log.error('Failed to fetch post', err)
-    }
-  }
-
-  // loader functions
-  // =
-
-  async _load() {
-    this._xLoading()
-    try {
-      const urip = new AtUri(this.uri)
-      const res = await this.rootStore.agent.getPost({
-        repo: urip.host,
-        rkey: urip.rkey,
-      })
-      // TODO
-      // if (!res.valid) {
-      //   throw new Error(res.error)
-      // }
-      this._replaceAll(res.value)
-      this._xIdle()
-    } catch (e: any) {
-      this._xIdle(e)
-    }
-  }
-
-  _replaceAll(res: Post.Record) {
-    this.text = res.text
-    this.entities = res.entities
-    this.reply = res.reply
-    this.createdAt = res.createdAt
-  }
-}
diff --git a/src/state/models/content/profile.ts b/src/state/models/content/profile.ts
index c26dc8749..ea75d19c6 100644
--- a/src/state/models/content/profile.ts
+++ b/src/state/models/content/profile.ts
@@ -10,6 +10,12 @@ import * as apilib from 'lib/api/index'
 import {cleanError} from 'lib/strings/errors'
 import {FollowState} from '../cache/my-follows'
 import {Image as RNImage} from 'react-native-image-crop-picker'
+import {ProfileLabelInfo, ProfileModeration} from 'lib/labeling/types'
+import {
+  getProfileModeration,
+  filterAccountLabels,
+  filterProfileLabels,
+} from 'lib/labeling/helpers'
 
 export const ACTOR_TYPE_USER = 'app.bsky.system.actorUser'
 
@@ -75,6 +81,18 @@ export class ProfileModel {
     return this.hasLoaded && !this.hasContent
   }
 
+  get labelInfo(): ProfileLabelInfo {
+    return {
+      accountLabels: filterAccountLabels(this.labels),
+      profileLabels: filterProfileLabels(this.labels),
+      isMuted: this.viewer?.muted || false,
+    }
+  }
+
+  get moderation(): ProfileModeration {
+    return getProfileModeration(this.rootStore, this.labelInfo)
+  }
+
   // public api
   // =
 
diff --git a/src/state/models/discovery/suggested-posts.ts b/src/state/models/discovery/suggested-posts.ts
deleted file mode 100644
index 6c8de3023..000000000
--- a/src/state/models/discovery/suggested-posts.ts
+++ /dev/null
@@ -1,88 +0,0 @@
-import {makeAutoObservable, runInAction} from 'mobx'
-import {RootStoreModel} from '../root-store'
-import {PostsFeedItemModel} from '../feeds/posts'
-import {cleanError} from 'lib/strings/errors'
-import {TEAM_HANDLES} from 'lib/constants'
-import {
-  getMultipleAuthorsPosts,
-  mergePosts,
-} from 'lib/api/build-suggested-posts'
-
-export class SuggestedPostsModel {
-  // state
-  isLoading = false
-  hasLoaded = false
-  error = ''
-
-  // data
-  posts: PostsFeedItemModel[] = []
-
-  constructor(public rootStore: RootStoreModel) {
-    makeAutoObservable(
-      this,
-      {
-        rootStore: false,
-      },
-      {autoBind: true},
-    )
-  }
-
-  get hasContent() {
-    return this.posts.length > 0
-  }
-
-  get hasError() {
-    return this.error !== ''
-  }
-
-  get isEmpty() {
-    return this.hasLoaded && !this.hasContent
-  }
-
-  // public api
-  // =
-
-  async setup() {
-    this._xLoading()
-    try {
-      const responses = await getMultipleAuthorsPosts(
-        this.rootStore,
-        TEAM_HANDLES(String(this.rootStore.agent.service)),
-        undefined,
-        30,
-      )
-      runInAction(() => {
-        const finalPosts = mergePosts(responses, {repostsOnly: true})
-        // hydrate into models
-        this.posts = finalPosts.map((post, i) => {
-          // strip the reasons to hide that these are reposts
-          delete post.reason
-          return new PostsFeedItemModel(this.rootStore, `post-${i}`, post)
-        })
-      })
-      this._xIdle()
-    } catch (e: any) {
-      this.rootStore.log.error('SuggestedPostsView: Failed to load posts', {
-        e,
-      })
-      this._xIdle() // dont bubble to the user
-    }
-  }
-
-  // state transitions
-  // =
-
-  _xLoading() {
-    this.isLoading = true
-    this.error = ''
-  }
-
-  _xIdle(err?: any) {
-    this.isLoading = false
-    this.hasLoaded = true
-    this.error = cleanError(err)
-    if (err) {
-      this.rootStore.log.error('Failed to fetch suggested posts', err)
-    }
-  }
-}
diff --git a/src/state/models/feeds/notifications.ts b/src/state/models/feeds/notifications.ts
index 220e04bce..02f58819f 100644
--- a/src/state/models/feeds/notifications.ts
+++ b/src/state/models/feeds/notifications.ts
@@ -15,6 +15,16 @@ import {bundleAsync} from 'lib/async/bundle'
 import {RootStoreModel} from '../root-store'
 import {PostThreadModel} from '../content/post-thread'
 import {cleanError} from 'lib/strings/errors'
+import {
+  PostLabelInfo,
+  PostModeration,
+  ModerationBehaviorCode,
+} from 'lib/labeling/types'
+import {
+  getPostModeration,
+  filterAccountLabels,
+  filterProfileLabels,
+} from 'lib/labeling/helpers'
 
 const GROUPABLE_REASONS = ['like', 'repost', 'follow']
 const PAGE_SIZE = 30
@@ -90,6 +100,24 @@ export class NotificationsFeedItemModel {
     }
   }
 
+  get labelInfo(): PostLabelInfo {
+    const addedInfo = this.additionalPost?.thread?.labelInfo
+    return {
+      postLabels: (this.labels || []).concat(addedInfo?.postLabels || []),
+      accountLabels: filterAccountLabels(this.author.labels).concat(
+        addedInfo?.accountLabels || [],
+      ),
+      profileLabels: filterProfileLabels(this.author.labels).concat(
+        addedInfo?.profileLabels || [],
+      ),
+      isMuted: this.author.viewer?.muted || addedInfo?.isMuted || false,
+    }
+  }
+
+  get moderation(): PostModeration {
+    return getPostModeration(this.rootStore, this.labelInfo)
+  }
+
   get numUnreadInGroup(): number {
     if (this.additional?.length) {
       return (
@@ -520,16 +548,22 @@ export class NotificationsFeedModel {
   _filterNotifications(
     items: NotificationsFeedItemModel[],
   ): NotificationsFeedItemModel[] {
-    return items.filter(item => {
-      const hideByLabel =
-        this.rootStore.preferences.getLabelPreference(item.labels).pref ===
-        'hide'
-      let mutedThread = !!(
-        item.reasonSubjectRootUri &&
-        this.rootStore.mutedThreads.uris.has(item.reasonSubjectRootUri)
-      )
-      return !hideByLabel && !mutedThread
-    })
+    return items
+      .filter(item => {
+        const hideByLabel =
+          item.moderation.list.behavior === ModerationBehaviorCode.Hide
+        let mutedThread = !!(
+          item.reasonSubjectRootUri &&
+          this.rootStore.mutedThreads.uris.has(item.reasonSubjectRootUri)
+        )
+        return !hideByLabel && !mutedThread
+      })
+      .map(item => {
+        if (item.additional?.length) {
+          item.additional = this._filterNotifications(item.additional)
+        }
+        return item
+      })
   }
 
   async _fetchItemModels(
diff --git a/src/state/models/feeds/posts.ts b/src/state/models/feeds/posts.ts
index cbff707d0..62c6da3de 100644
--- a/src/state/models/feeds/posts.ts
+++ b/src/state/models/feeds/posts.ts
@@ -20,6 +20,13 @@ import {
 } from 'lib/api/build-suggested-posts'
 import {FeedTuner, FeedViewPostsSlice} from 'lib/api/feed-manip'
 import {updateDataOptimistically} from 'lib/async/revertible'
+import {PostLabelInfo, PostModeration} from 'lib/labeling/types'
+import {
+  getEmbedLabels,
+  getPostModeration,
+  filterAccountLabels,
+  filterProfileLabels,
+} from 'lib/labeling/helpers'
 
 type FeedViewPost = AppBskyFeedDefs.FeedViewPost
 type ReasonRepost = AppBskyFeedDefs.ReasonRepost
@@ -83,6 +90,21 @@ export class PostsFeedItemModel {
     return this.rootStore.mutedThreads.uris.has(this.rootUri)
   }
 
+  get labelInfo(): PostLabelInfo {
+    return {
+      postLabels: (this.post.labels || []).concat(
+        getEmbedLabels(this.post.embed),
+      ),
+      accountLabels: filterAccountLabels(this.post.author.labels),
+      profileLabels: filterProfileLabels(this.post.author.labels),
+      isMuted: this.post.author.viewer?.muted || false,
+    }
+  }
+
+  get moderation(): PostModeration {
+    return getPostModeration(this.rootStore, this.labelInfo)
+  }
+
   copy(v: FeedViewPost) {
     this.post = v.post
     this.reply = v.reply