]> Untitled Git - lemmy.git/blob - crates/api/src/post.rs
Split api crate into api_structs and api
[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 = match blocking(context.pool(), move |conn| {
75       PostView::read(conn, post_id, Some(person_id))
76     })
77     .await?
78     {
79       Ok(post) => post,
80       Err(_e) => return Err(ApiError::err("couldnt_find_post").into()),
81     };
82
83     let res = PostResponse { post_view };
84
85     context.chat_server().do_send(SendPost {
86       op: UserOperation::CreatePostLike,
87       post: res.clone(),
88       websocket_id,
89     });
90
91     Ok(res)
92   }
93 }
94
95 #[async_trait::async_trait(?Send)]
96 impl Perform for LockPost {
97   type Response = PostResponse;
98
99   async fn perform(
100     &self,
101     context: &Data<LemmyContext>,
102     websocket_id: Option<ConnectionId>,
103   ) -> Result<PostResponse, LemmyError> {
104     let data: &LockPost = &self;
105     let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
106
107     let post_id = data.post_id;
108     let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
109
110     check_community_ban(
111       local_user_view.person.id,
112       orig_post.community_id,
113       context.pool(),
114     )
115     .await?;
116
117     // Verify that only the mods can lock
118     is_mod_or_admin(
119       context.pool(),
120       local_user_view.person.id,
121       orig_post.community_id,
122     )
123     .await?;
124
125     // Update the post
126     let post_id = data.post_id;
127     let locked = data.locked;
128     let updated_post = blocking(context.pool(), move |conn| {
129       Post::update_locked(conn, post_id, locked)
130     })
131     .await??;
132
133     // Mod tables
134     let form = ModLockPostForm {
135       mod_person_id: local_user_view.person.id,
136       post_id: data.post_id,
137       locked: Some(locked),
138     };
139     blocking(context.pool(), move |conn| ModLockPost::create(conn, &form)).await??;
140
141     // apub updates
142     updated_post
143       .send_update(&local_user_view.person, context)
144       .await?;
145
146     // Refetch the post
147     let post_id = data.post_id;
148     let post_view = blocking(context.pool(), move |conn| {
149       PostView::read(conn, post_id, Some(local_user_view.person.id))
150     })
151     .await??;
152
153     let res = PostResponse { post_view };
154
155     context.chat_server().do_send(SendPost {
156       op: UserOperation::LockPost,
157       post: res.clone(),
158       websocket_id,
159     });
160
161     Ok(res)
162   }
163 }
164
165 #[async_trait::async_trait(?Send)]
166 impl Perform for StickyPost {
167   type Response = PostResponse;
168
169   async fn perform(
170     &self,
171     context: &Data<LemmyContext>,
172     websocket_id: Option<ConnectionId>,
173   ) -> Result<PostResponse, LemmyError> {
174     let data: &StickyPost = &self;
175     let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
176
177     let post_id = data.post_id;
178     let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
179
180     check_community_ban(
181       local_user_view.person.id,
182       orig_post.community_id,
183       context.pool(),
184     )
185     .await?;
186
187     // Verify that only the mods can sticky
188     is_mod_or_admin(
189       context.pool(),
190       local_user_view.person.id,
191       orig_post.community_id,
192     )
193     .await?;
194
195     // Update the post
196     let post_id = data.post_id;
197     let stickied = data.stickied;
198     let updated_post = blocking(context.pool(), move |conn| {
199       Post::update_stickied(conn, post_id, stickied)
200     })
201     .await??;
202
203     // Mod tables
204     let form = ModStickyPostForm {
205       mod_person_id: local_user_view.person.id,
206       post_id: data.post_id,
207       stickied: Some(stickied),
208     };
209     blocking(context.pool(), move |conn| {
210       ModStickyPost::create(conn, &form)
211     })
212     .await??;
213
214     // Apub updates
215     // TODO stickied should pry work like locked for ease of use
216     updated_post
217       .send_update(&local_user_view.person, context)
218       .await?;
219
220     // Refetch the post
221     let post_id = data.post_id;
222     let post_view = blocking(context.pool(), move |conn| {
223       PostView::read(conn, post_id, Some(local_user_view.person.id))
224     })
225     .await??;
226
227     let res = PostResponse { post_view };
228
229     context.chat_server().do_send(SendPost {
230       op: UserOperation::StickyPost,
231       post: res.clone(),
232       websocket_id,
233     });
234
235     Ok(res)
236   }
237 }
238
239 #[async_trait::async_trait(?Send)]
240 impl Perform for SavePost {
241   type Response = PostResponse;
242
243   async fn perform(
244     &self,
245     context: &Data<LemmyContext>,
246     _websocket_id: Option<ConnectionId>,
247   ) -> Result<PostResponse, LemmyError> {
248     let data: &SavePost = &self;
249     let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
250
251     let post_saved_form = PostSavedForm {
252       post_id: data.post_id,
253       person_id: local_user_view.person.id,
254     };
255
256     if data.save {
257       let save = move |conn: &'_ _| PostSaved::save(conn, &post_saved_form);
258       if blocking(context.pool(), save).await?.is_err() {
259         return Err(ApiError::err("couldnt_save_post").into());
260       }
261     } else {
262       let unsave = move |conn: &'_ _| PostSaved::unsave(conn, &post_saved_form);
263       if blocking(context.pool(), unsave).await?.is_err() {
264         return Err(ApiError::err("couldnt_save_post").into());
265       }
266     }
267
268     let post_id = data.post_id;
269     let person_id = local_user_view.person.id;
270     let post_view = blocking(context.pool(), move |conn| {
271       PostView::read(conn, post_id, Some(person_id))
272     })
273     .await??;
274
275     Ok(PostResponse { post_view })
276   }
277 }