2 use actix_web::web::Data;
3 use lemmy_api_common::{
6 check_downvotes_enabled,
8 get_local_user_view_from_jwt,
15 post::create_or_update::CreateOrUpdatePost,
18 vote::{Vote, VoteType},
24 use lemmy_db_queries::{source::post::Post_, Crud, Likeable, Saveable};
25 use lemmy_db_schema::source::{moderator::*, post::*};
26 use lemmy_db_views::post_view::PostView;
27 use lemmy_utils::{request::fetch_site_metadata, ApiError, ConnectionId, LemmyError};
28 use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperation};
29 use std::convert::TryInto;
31 #[async_trait::async_trait(?Send)]
32 impl Perform for CreatePostLike {
33 type Response = PostResponse;
37 context: &Data<LemmyContext>,
38 websocket_id: Option<ConnectionId>,
39 ) -> Result<PostResponse, LemmyError> {
40 let data: &CreatePostLike = self;
41 let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
43 // Don't do a downvote if site has downvotes disabled
44 check_downvotes_enabled(data.score, context.pool()).await?;
46 // Check for a community ban
47 let post_id = data.post_id;
48 let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
50 check_community_ban(local_user_view.person.id, post.community_id, context.pool()).await?;
52 check_person_block(local_user_view.person.id, post.creator_id, context.pool()).await?;
54 let like_form = PostLikeForm {
55 post_id: data.post_id,
56 person_id: local_user_view.person.id,
60 // Remove any likes first
61 let person_id = local_user_view.person.id;
62 blocking(context.pool(), move |conn| {
63 PostLike::remove(conn, person_id, post_id)
67 let community_id = post.community_id;
68 let object = PostOrComment::Post(Box::new(post));
70 // Only add the like if the score isnt 0
71 let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
73 let like_form2 = like_form.clone();
74 let like = move |conn: &'_ _| PostLike::like(conn, &like_form2);
75 if blocking(context.pool(), like).await?.is_err() {
76 return Err(ApiError::err("couldnt_like_post").into());
81 &local_user_view.person,
83 like_form.score.try_into()?,
88 // API doesn't distinguish between Undo/Like and Undo/Dislike
91 &local_user_view.person,
99 // Mark the post as read
100 mark_post_as_read(person_id, post_id, context.pool()).await?;
102 send_post_ws_message(
104 UserOperation::CreatePostLike,
106 Some(local_user_view.person.id),
113 #[async_trait::async_trait(?Send)]
114 impl Perform for LockPost {
115 type Response = PostResponse;
119 context: &Data<LemmyContext>,
120 websocket_id: Option<ConnectionId>,
121 ) -> Result<PostResponse, LemmyError> {
122 let data: &LockPost = self;
123 let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
125 let post_id = data.post_id;
126 let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
129 local_user_view.person.id,
130 orig_post.community_id,
135 // Verify that only the mods can lock
138 local_user_view.person.id,
139 orig_post.community_id,
144 let post_id = data.post_id;
145 let locked = data.locked;
146 let updated_post = blocking(context.pool(), move |conn| {
147 Post::update_locked(conn, post_id, locked)
152 let form = ModLockPostForm {
153 mod_person_id: local_user_view.person.id,
154 post_id: data.post_id,
155 locked: Some(locked),
157 blocking(context.pool(), move |conn| ModLockPost::create(conn, &form)).await??;
160 CreateOrUpdatePost::send(
162 &local_user_view.person,
163 CreateOrUpdateType::Update,
168 send_post_ws_message(
170 UserOperation::LockPost,
172 Some(local_user_view.person.id),
179 #[async_trait::async_trait(?Send)]
180 impl Perform for StickyPost {
181 type Response = PostResponse;
185 context: &Data<LemmyContext>,
186 websocket_id: Option<ConnectionId>,
187 ) -> Result<PostResponse, LemmyError> {
188 let data: &StickyPost = self;
189 let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
191 let post_id = data.post_id;
192 let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
195 local_user_view.person.id,
196 orig_post.community_id,
201 // Verify that only the mods can sticky
204 local_user_view.person.id,
205 orig_post.community_id,
210 let post_id = data.post_id;
211 let stickied = data.stickied;
212 let updated_post = blocking(context.pool(), move |conn| {
213 Post::update_stickied(conn, post_id, stickied)
218 let form = ModStickyPostForm {
219 mod_person_id: local_user_view.person.id,
220 post_id: data.post_id,
221 stickied: Some(stickied),
223 blocking(context.pool(), move |conn| {
224 ModStickyPost::create(conn, &form)
229 // TODO stickied should pry work like locked for ease of use
230 CreateOrUpdatePost::send(
232 &local_user_view.person,
233 CreateOrUpdateType::Update,
238 send_post_ws_message(
240 UserOperation::StickyPost,
242 Some(local_user_view.person.id),
249 #[async_trait::async_trait(?Send)]
250 impl Perform for SavePost {
251 type Response = PostResponse;
255 context: &Data<LemmyContext>,
256 _websocket_id: Option<ConnectionId>,
257 ) -> Result<PostResponse, LemmyError> {
258 let data: &SavePost = self;
259 let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
261 let post_saved_form = PostSavedForm {
262 post_id: data.post_id,
263 person_id: local_user_view.person.id,
267 let save = move |conn: &'_ _| PostSaved::save(conn, &post_saved_form);
268 if blocking(context.pool(), save).await?.is_err() {
269 return Err(ApiError::err("couldnt_save_post").into());
272 let unsave = move |conn: &'_ _| PostSaved::unsave(conn, &post_saved_form);
273 if blocking(context.pool(), unsave).await?.is_err() {
274 return Err(ApiError::err("couldnt_save_post").into());
278 let post_id = data.post_id;
279 let person_id = local_user_view.person.id;
280 let post_view = blocking(context.pool(), move |conn| {
281 PostView::read(conn, post_id, Some(person_id))
285 // Mark the post as read
286 mark_post_as_read(person_id, post_id, context.pool()).await?;
288 Ok(PostResponse { post_view })
292 #[async_trait::async_trait(?Send)]
293 impl Perform for GetSiteMetadata {
294 type Response = GetSiteMetadataResponse;
298 context: &Data<LemmyContext>,
299 _websocket_id: Option<ConnectionId>,
300 ) -> Result<GetSiteMetadataResponse, LemmyError> {
301 let data: &Self = self;
303 let metadata = fetch_site_metadata(context.client(), &data.url).await?;
305 Ok(GetSiteMetadataResponse { metadata })