]> Untitled Git - lemmy.git/blob - crates/api/src/post.rs
34c9c4116a1bc09bd113cbe70cd2250496cf71da
[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   post::*,
10 };
11 use lemmy_apub::{ApubLikeableType, ApubObjectType};
12 use lemmy_db_queries::{source::post::Post_, Crud, Likeable, Saveable};
13 use lemmy_db_schema::source::{moderator::*, post::*};
14 use lemmy_db_views::post_view::PostView;
15 use lemmy_utils::{ApiError, ConnectionId, LemmyError};
16 use lemmy_websocket::{messages::SendPost, LemmyContext, UserOperation};
17
18 #[async_trait::async_trait(?Send)]
19 impl Perform for CreatePostLike {
20   type Response = PostResponse;
21
22   async fn perform(
23     &self,
24     context: &Data<LemmyContext>,
25     websocket_id: Option<ConnectionId>,
26   ) -> Result<PostResponse, LemmyError> {
27     let data: &CreatePostLike = &self;
28     let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
29
30     // Don't do a downvote if site has downvotes disabled
31     check_downvotes_enabled(data.score, context.pool()).await?;
32
33     // Check for a community ban
34     let post_id = data.post_id;
35     let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
36
37     check_community_ban(local_user_view.person.id, post.community_id, context.pool()).await?;
38
39     let like_form = PostLikeForm {
40       post_id: data.post_id,
41       person_id: local_user_view.person.id,
42       score: data.score,
43     };
44
45     // Remove any likes first
46     let person_id = local_user_view.person.id;
47     blocking(context.pool(), move |conn| {
48       PostLike::remove(conn, person_id, post_id)
49     })
50     .await??;
51
52     // Only add the like if the score isnt 0
53     let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
54     if do_add {
55       let like_form2 = like_form.clone();
56       let like = move |conn: &'_ _| PostLike::like(conn, &like_form2);
57       if blocking(context.pool(), like).await?.is_err() {
58         return Err(ApiError::err("couldnt_like_post").into());
59       }
60
61       if like_form.score == 1 {
62         post.send_like(&local_user_view.person, context).await?;
63       } else if like_form.score == -1 {
64         post.send_dislike(&local_user_view.person, context).await?;
65       }
66     } else {
67       post
68         .send_undo_like(&local_user_view.person, context)
69         .await?;
70     }
71
72     let post_id = data.post_id;
73     let person_id = local_user_view.person.id;
74     let post_view = blocking(context.pool(), move |conn| {
75       PostView::read(conn, post_id, Some(person_id))
76     })
77     .await?
78     .map_err(|_| ApiError::err("couldnt_find_post"))?;
79
80     let res = PostResponse { post_view };
81
82     context.chat_server().do_send(SendPost {
83       op: UserOperation::CreatePostLike,
84       post: res.clone(),
85       websocket_id,
86     });
87
88     Ok(res)
89   }
90 }
91
92 #[async_trait::async_trait(?Send)]
93 impl Perform for LockPost {
94   type Response = PostResponse;
95
96   async fn perform(
97     &self,
98     context: &Data<LemmyContext>,
99     websocket_id: Option<ConnectionId>,
100   ) -> Result<PostResponse, LemmyError> {
101     let data: &LockPost = &self;
102     let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
103
104     let post_id = data.post_id;
105     let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
106
107     check_community_ban(
108       local_user_view.person.id,
109       orig_post.community_id,
110       context.pool(),
111     )
112     .await?;
113
114     // Verify that only the mods can lock
115     is_mod_or_admin(
116       context.pool(),
117       local_user_view.person.id,
118       orig_post.community_id,
119     )
120     .await?;
121
122     // Update the post
123     let post_id = data.post_id;
124     let locked = data.locked;
125     let updated_post = blocking(context.pool(), move |conn| {
126       Post::update_locked(conn, post_id, locked)
127     })
128     .await??;
129
130     // Mod tables
131     let form = ModLockPostForm {
132       mod_person_id: local_user_view.person.id,
133       post_id: data.post_id,
134       locked: Some(locked),
135     };
136     blocking(context.pool(), move |conn| ModLockPost::create(conn, &form)).await??;
137
138     // apub updates
139     updated_post
140       .send_update(&local_user_view.person, context)
141       .await?;
142
143     // Refetch the post
144     let post_id = data.post_id;
145     let post_view = blocking(context.pool(), move |conn| {
146       PostView::read(conn, post_id, Some(local_user_view.person.id))
147     })
148     .await??;
149
150     let res = PostResponse { post_view };
151
152     context.chat_server().do_send(SendPost {
153       op: UserOperation::LockPost,
154       post: res.clone(),
155       websocket_id,
156     });
157
158     Ok(res)
159   }
160 }
161
162 #[async_trait::async_trait(?Send)]
163 impl Perform for StickyPost {
164   type Response = PostResponse;
165
166   async fn perform(
167     &self,
168     context: &Data<LemmyContext>,
169     websocket_id: Option<ConnectionId>,
170   ) -> Result<PostResponse, LemmyError> {
171     let data: &StickyPost = &self;
172     let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
173
174     let post_id = data.post_id;
175     let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
176
177     check_community_ban(
178       local_user_view.person.id,
179       orig_post.community_id,
180       context.pool(),
181     )
182     .await?;
183
184     // Verify that only the mods can sticky
185     is_mod_or_admin(
186       context.pool(),
187       local_user_view.person.id,
188       orig_post.community_id,
189     )
190     .await?;
191
192     // Update the post
193     let post_id = data.post_id;
194     let stickied = data.stickied;
195     let updated_post = blocking(context.pool(), move |conn| {
196       Post::update_stickied(conn, post_id, stickied)
197     })
198     .await??;
199
200     // Mod tables
201     let form = ModStickyPostForm {
202       mod_person_id: local_user_view.person.id,
203       post_id: data.post_id,
204       stickied: Some(stickied),
205     };
206     blocking(context.pool(), move |conn| {
207       ModStickyPost::create(conn, &form)
208     })
209     .await??;
210
211     // Apub updates
212     // TODO stickied should pry work like locked for ease of use
213     updated_post
214       .send_update(&local_user_view.person, context)
215       .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     Ok(PostResponse { post_view })
273   }
274 }