about summary refs log tree commit diff
path: root/src/state/models/content/post-thread-item.ts
blob: 14aa607ed5cb8866473cdc7b04b51be805fd3192 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import {makeAutoObservable} from 'mobx'
import {
  AppBskyFeedPost as FeedPost,
  AppBskyFeedDefs,
  RichText,
} from '@atproto/api'
import {RootStoreModel} from '../root-store'
import {PostLabelInfo, PostModeration} from 'lib/labeling/types'
import {PostsFeedItemModel} from '../feeds/post'

type PostView = AppBskyFeedDefs.PostView

// NOTE: this model uses the same data as PostsFeedItemModel, but is used for
// rendering a single post in a thread view, and has additional state
// for rendering the thread view, but calls the same data methods
// as PostsFeedItemModel
// TODO: refactor as an extension or subclass of PostsFeedItemModel
export class PostThreadItemModel {
  // ui state
  _reactKey: string = ''
  _depth = 0
  _isHighlightedPost = false
  _showParentReplyLine = false
  _showChildReplyLine = false
  _hasMore = false

  // data
  data: PostsFeedItemModel
  post: PostView
  postRecord?: FeedPost.Record
  richText?: RichText
  parent?:
    | PostThreadItemModel
    | AppBskyFeedDefs.NotFoundPost
    | AppBskyFeedDefs.BlockedPost
  replies?: (PostThreadItemModel | AppBskyFeedDefs.NotFoundPost)[]

  constructor(
    public rootStore: RootStoreModel,
    v: AppBskyFeedDefs.ThreadViewPost,
  ) {
    this._reactKey = `thread-${v.post.uri}`
    this.data = new PostsFeedItemModel(rootStore, this._reactKey, v)
    this.post = this.data.post
    this.postRecord = this.data.postRecord
    this.richText = this.data.richText
    // replies and parent are handled via assignTreeModels
    makeAutoObservable(this, {rootStore: false})
  }

  get uri() {
    return this.post.uri
  }

  get parentUri() {
    return this.postRecord?.reply?.parent.uri
  }

  get rootUri(): string {
    if (this.postRecord?.reply?.root.uri) {
      return this.postRecord.reply.root.uri
    }
    return this.post.uri
  }

  get isThreadMuted() {
    return this.data.isThreadMuted
  }

  get labelInfo(): PostLabelInfo {
    return this.data.labelInfo
  }

  get moderation(): PostModeration {
    return this.data.moderation
  }

  assignTreeModels(
    v: AppBskyFeedDefs.ThreadViewPost,
    highlightedPostUri: string,
    includeParent = true,
    includeChildren = true,
  ) {
    // parents
    if (includeParent && v.parent) {
      if (AppBskyFeedDefs.isThreadViewPost(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.assignTreeModels(
            v.parent,
            highlightedPostUri,
            true,
            false,
          )
        }
        this.parent = parentModel
      } else if (AppBskyFeedDefs.isNotFoundPost(v.parent)) {
        this.parent = v.parent
      } else if (AppBskyFeedDefs.isBlockedPost(v.parent)) {
        this.parent = v.parent
      }
    }
    // replies
    if (includeChildren && v.replies) {
      const replies = []
      for (const item of v.replies) {
        if (AppBskyFeedDefs.isThreadViewPost(item)) {
          const itemModel = new PostThreadItemModel(this.rootStore, item)
          itemModel._depth = this._depth + 1
          itemModel._showParentReplyLine =
            itemModel.parentUri !== highlightedPostUri && replies.length === 0
          if (item.replies?.length) {
            itemModel._showChildReplyLine = true
            itemModel.assignTreeModels(item, highlightedPostUri, false, true)
          }
          replies.push(itemModel)
        } else if (AppBskyFeedDefs.isNotFoundPost(item)) {
          replies.push(item)
        }
      }
      this.replies = replies
    }
  }

  async toggleLike() {
    this.data.toggleLike()
  }

  async toggleRepost() {
    this.data.toggleRepost()
  }

  async toggleThreadMute() {
    this.data.toggleThreadMute()
  }

  async delete() {
    this.data.delete()
  }
}