]> Untitled Git - lemmy.git/blob - crates/api/src/post.rs
Merge pull request #1678 from LemmyNet/rewrite-post
[lemmy.git] / crates / api / src / post.rs
1 use crate::Perform;
2 use actix_web::web::Data;
3 use lemmy_api_common::{
4   blocking,
5   check_community_ban,
6   check_downvotes_enabled,
7   get_local_user_view_from_jwt,
8   is_mod_or_admin,
9   mark_post_as_read,
10   post::*,
11 };
12 use lemmy_apub::{activities::post::update::UpdatePost, ApubLikeableType};
13 use lemmy_db_queries::{source::post::Post_, Crud, Likeable, Saveable};
14 use lemmy_db_schema::source::{moderator::*, post::*};
15 use lemmy_db_views::post_view::PostView;
16 use lemmy_utils::{ApiError, ConnectionId, LemmyError};
17 use lemmy_websocket::{messages::SendPost, LemmyContext, UserOperation};
18
19 #[async_trait::async_trait(?Send)]
20 impl Perform for CreatePostLike {
21   type Response = PostResponse;
22
23   async fn perform(
24     &self,
25     context: &Data<LemmyContext>,
26     websocket_id: Option<ConnectionId>,
27   ) -> Result<PostResponse, LemmyError> {
28     let data: &CreatePostLike = self;
29     let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
30
31     // Don't do a downvote if site has downvotes disabled
32     check_downvotes_enabled(data.score, context.pool()).await?;
33
34     // Check for a community ban
35     let post_id = data.post_id;
36     let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
37
38     check_community_ban(local_user_view.person.id, post.community_id, context.pool()).await?;
39
40     let like_form = PostLikeForm {
41       post_id: data.post_id,
42       person_id: local_user_view.person.id,
43       score: data.score,
44     };
45
46     // Remove any likes first
47     let person_id = local_user_view.person.id;
48     blocking(context.pool(), move |conn| {
49       PostLike::remove(conn, person_id, post_id)
50     })
51     .await??;
52
53     // Only add the like if the score isnt 0
54     let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
55     if do_add {
56       let like_form2 = like_form.clone();
57       let like = move |conn: &'_ _| PostLike::like(conn, &like_form2);
58       if blocking(context.pool(), like).await?.is_err() {
59         return Err(ApiError::err("couldnt_like_post").into());
60       }
61
62       if like_form.score == 1 {
63         post.send_like(&local_user_view.person, context).await?;
64       } else if like_form.score == -1 {
65         post.send_dislike(&local_user_view.person, context).await?;
66       }
67     } else {
68       post
69         .send_undo_like(&local_user_view.person, context)
70         .await?;
71     }
72
73     // Mark the post as read
74     mark_post_as_read(person_id, post_id, context.pool()).await?;
75
76     let post_id = data.post_id;
77     let person_id = local_user_view.person.id;
78     let post_view = blocking(context.pool(), move |conn| {
79       PostView::read(conn, post_id, Some(person_id))
80     })
81     .await?
82     .map_err(|_| ApiError::err("couldnt_find_post"))?;
83
84     let res = PostResponse { post_view };
85
86     context.chat_server().do_send(SendPost {
87       op: UserOperation::CreatePostLike,
88       post: res.clone(),
89       websocket_id,
90     });
91
92     Ok(res)
93   }
94 }
95
96 #[async_trait::async_trait(?Send)]
97 impl Perform for LockPost {
98   type Response = PostResponse;
99
100   async fn perform(
101     &self,
102     context: &Data<LemmyContext>,
103     websocket_id: Option<ConnectionId>,
104   ) -> Result<PostResponse, LemmyError> {
105     let data: &LockPost = self;
106     let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
107
108     let post_id = data.post_id;
109     let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
110
111     check_community_ban(
112       local_user_view.person.id,
113       orig_post.community_id,
114       context.pool(),
115     )
116     .await?;
117
118     // Verify that only the mods can lock
119     is_mod_or_admin(
120       context.pool(),
121       local_user_view.person.id,
122       orig_post.community_id,
123     )
124     .await?;
125
126     // Update the post
127     let post_id = data.post_id;
128     let locked = data.locked;
129     let updated_post = blocking(context.pool(), move |conn| {
130       Post::update_locked(conn, post_id, locked)
131     })
132     .await??;
133
134     // Mod tables
135     let form = ModLockPostForm {
136       mod_person_id: local_user_view.person.id,
137       post_id: data.post_id,
138       locked: Some(locked),
139     };
140     blocking(context.pool(), move |conn| ModLockPost::create(conn, &form)).await??;
141
142     // apub updates
143     UpdatePost::send(&updated_post, &local_user_view.person, context).await?;
144
145     // Refetch the post
146     let post_id = data.post_id;
147     let post_view = blocking(context.pool(), move |conn| {
148       PostView::read(conn, post_id, Some(local_user_view.person.id))
149     })
150     .await??;
151
152     let res = PostResponse { post_view };
153
154     context.chat_server().do_send(SendPost {
155       op: UserOperation::LockPost,
156       post: res.clone(),
157       websocket_id,
158     });
159
160     Ok(res)
161   }
162 }
163
164 #[async_trait::async_trait(?Send)]
165 impl Perform for StickyPost {
166   type Response = PostResponse;
167
168   async fn perform(
169     &self,
170     context: &Data<LemmyContext>,
171     websocket_id: Option<ConnectionId>,
172   ) -> Result<PostResponse, LemmyError> {
173     let data: &StickyPost = self;
174     let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
175
176     let post_id = data.post_id;
177     let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
178
179     check_community_ban(
180       local_user_view.person.id,
181       orig_post.community_id,
182       context.pool(),
183     )
184     .await?;
185
186     // Verify that only the mods can sticky
187     is_mod_or_admin(
188       context.pool(),
189       local_user_view.person.id,
190       orig_post.community_id,
191     )
192     .await?;
193
194     // Update the post
195     let post_id = data.post_id;
196     let stickied = data.stickied;
197     let updated_post = blocking(context.pool(), move |conn| {
198       Post::update_stickied(conn, post_id, stickied)
199     })
200     .await??;
201
202     // Mod tables
203     let form = ModStickyPostForm {
204       mod_person_id: local_user_view.person.id,
205       post_id: data.post_id,
206       stickied: Some(stickied),
207     };
208     blocking(context.pool(), move |conn| {
209       ModStickyPost::create(conn, &form)
210     })
211     .await??;
212
213     // Apub updates
214     // TODO stickied should pry work like locked for ease of use
215     UpdatePost::send(&updated_post, &local_user_view.person, context).await?;
216
217     // Refetch the post
218     let post_id = data.post_id;
219     let post_view = blocking(context.pool(), move |conn| {
220       PostView::read(conn, post_id, Some(local_user_view.person.id))
221     })
222     .await??;
223
224     let res = PostResponse { post_view };
225
226     context.chat_server().do_send(SendPost {
227       op: UserOperation::StickyPost,
228       post: res.clone(),
229       websocket_id,
230     });
231
232     Ok(res)
233   }
234 }
235
236 #[async_trait::async_trait(?Send)]
237 impl Perform for SavePost {
238   type Response = PostResponse;
239
240   async fn perform(
241     &self,
242     context: &Data<LemmyContext>,
243     _websocket_id: Option<ConnectionId>,
244   ) -> Result<PostResponse, LemmyError> {
245     let data: &SavePost = self;
246     let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
247
248     let post_saved_form = PostSavedForm {
249       post_id: data.post_id,
250       person_id: local_user_view.person.id,
251     };
252
253     if data.save {
254       let save = move |conn: &'_ _| PostSaved::save(conn, &post_saved_form);
255       if blocking(context.pool(), save).await?.is_err() {
256         return Err(ApiError::err("couldnt_save_post").into());
257       }
258     } else {
259       let unsave = move |conn: &'_ _| PostSaved::unsave(conn, &post_saved_form);
260       if blocking(context.pool(), unsave).await?.is_err() {
261         return Err(ApiError::err("couldnt_save_post").into());
262       }
263     }
264
265     let post_id = data.post_id;
266     let person_id = local_user_view.person.id;
267     let post_view = blocking(context.pool(), move |conn| {
268       PostView::read(conn, post_id, Some(person_id))
269     })
270     .await??;
271
272     // Mark the post as read
273     mark_post_as_read(person_id, post_id, context.pool()).await?;
274
275     Ok(PostResponse { post_view })
276   }
277 }