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