mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import {makeAutoObservable} from 'mobx'
2import {
3 AppBskyFeedPost as FeedPost,
4 AppBskyFeedDefs,
5 RichText,
6 PostModeration,
7} from '@atproto/api'
8import {RootStoreModel} from '../root-store'
9import {PostsFeedItemModel} from '../feeds/post'
10
11type PostView = AppBskyFeedDefs.PostView
12
13// NOTE: this model uses the same data as PostsFeedItemModel, but is used for
14// rendering a single post in a thread view, and has additional state
15// for rendering the thread view, but calls the same data methods
16// as PostsFeedItemModel
17// TODO: refactor as an extension or subclass of PostsFeedItemModel
18export class PostThreadItemModel {
19 // ui state
20 _reactKey: string = ''
21 _depth = 0
22 _isHighlightedPost = false
23 _showParentReplyLine = false
24 _showChildReplyLine = false
25 _hasMore = false
26
27 // data
28 data: PostsFeedItemModel
29 post: PostView
30 postRecord?: FeedPost.Record
31 richText?: RichText
32 parent?:
33 | PostThreadItemModel
34 | AppBskyFeedDefs.NotFoundPost
35 | AppBskyFeedDefs.BlockedPost
36 replies?: (PostThreadItemModel | AppBskyFeedDefs.NotFoundPost)[]
37
38 constructor(
39 public rootStore: RootStoreModel,
40 v: AppBskyFeedDefs.ThreadViewPost,
41 ) {
42 this._reactKey = `thread-${v.post.uri}`
43 this.data = new PostsFeedItemModel(rootStore, this._reactKey, v)
44 this.post = this.data.post
45 this.postRecord = this.data.postRecord
46 this.richText = this.data.richText
47 // replies and parent are handled via assignTreeModels
48 makeAutoObservable(this, {rootStore: false})
49 }
50
51 get uri() {
52 return this.post.uri
53 }
54
55 get parentUri() {
56 return this.postRecord?.reply?.parent.uri
57 }
58
59 get rootUri(): string {
60 if (this.postRecord?.reply?.root.uri) {
61 return this.postRecord.reply.root.uri
62 }
63 return this.post.uri
64 }
65
66 get isThreadMuted() {
67 return this.data.isThreadMuted
68 }
69
70 get moderation(): PostModeration {
71 return this.data.moderation
72 }
73
74 assignTreeModels(
75 v: AppBskyFeedDefs.ThreadViewPost,
76 highlightedPostUri: string,
77 includeParent = true,
78 includeChildren = true,
79 ) {
80 // parents
81 if (includeParent && v.parent) {
82 if (AppBskyFeedDefs.isThreadViewPost(v.parent)) {
83 const parentModel = new PostThreadItemModel(this.rootStore, v.parent)
84 parentModel._depth = this._depth - 1
85 parentModel._showChildReplyLine = true
86 if (v.parent.parent) {
87 parentModel._showParentReplyLine = true
88 parentModel.assignTreeModels(
89 v.parent,
90 highlightedPostUri,
91 true,
92 false,
93 )
94 }
95 this.parent = parentModel
96 } else if (AppBskyFeedDefs.isNotFoundPost(v.parent)) {
97 this.parent = v.parent
98 } else if (AppBskyFeedDefs.isBlockedPost(v.parent)) {
99 this.parent = v.parent
100 }
101 }
102 // replies
103 if (includeChildren && v.replies) {
104 const replies = []
105 for (const item of v.replies) {
106 if (AppBskyFeedDefs.isThreadViewPost(item)) {
107 const itemModel = new PostThreadItemModel(this.rootStore, item)
108 itemModel._depth = this._depth + 1
109 itemModel._showParentReplyLine =
110 itemModel.parentUri !== highlightedPostUri
111 if (item.replies?.length) {
112 itemModel._showChildReplyLine = true
113 itemModel.assignTreeModels(item, highlightedPostUri, false, true)
114 }
115 replies.push(itemModel)
116 } else if (AppBskyFeedDefs.isNotFoundPost(item)) {
117 replies.push(item)
118 }
119 }
120 this.replies = replies
121 }
122 }
123
124 async toggleLike() {
125 this.data.toggleLike()
126 }
127
128 async toggleRepost() {
129 this.data.toggleRepost()
130 }
131
132 async toggleThreadMute() {
133 this.data.toggleThreadMute()
134 }
135
136 async delete() {
137 this.data.delete()
138 }
139}