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},
22 fetcher::post_or_comment::PostOrComment,
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;
42 get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
44 // Don't do a downvote if site has downvotes disabled
45 check_downvotes_enabled(data.score, context.pool()).await?;
47 // Check for a community ban
48 let post_id = data.post_id;
49 let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
51 check_community_ban(local_user_view.person.id, post.community_id, context.pool()).await?;
53 check_person_block(local_user_view.person.id, post.creator_id, context.pool()).await?;
55 let like_form = PostLikeForm {
56 post_id: data.post_id,
57 person_id: local_user_view.person.id,
61 // Remove any likes first
62 let person_id = local_user_view.person.id;
63 blocking(context.pool(), move |conn| {
64 PostLike::remove(conn, person_id, post_id)
68 let community_id = post.community_id;
69 let object = PostOrComment::Post(Box::new(post));
71 // Only add the like if the score isnt 0
72 let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
74 let like_form2 = like_form.clone();
75 let like = move |conn: &'_ _| PostLike::like(conn, &like_form2);
76 if blocking(context.pool(), like).await?.is_err() {
77 return Err(ApiError::err("couldnt_like_post").into());
82 &local_user_view.person,
84 like_form.score.try_into()?,
89 // API doesn't distinguish between Undo/Like and Undo/Dislike
92 &local_user_view.person,
100 // Mark the post as read
101 mark_post_as_read(person_id, post_id, context.pool()).await?;
103 send_post_ws_message(
105 UserOperation::CreatePostLike,
107 Some(local_user_view.person.id),
114 #[async_trait::async_trait(?Send)]
115 impl Perform for LockPost {
116 type Response = PostResponse;
120 context: &Data<LemmyContext>,
121 websocket_id: Option<ConnectionId>,
122 ) -> Result<PostResponse, LemmyError> {
123 let data: &LockPost = self;
124 let local_user_view =
125 get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
127 let post_id = data.post_id;
128 let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
131 local_user_view.person.id,
132 orig_post.community_id,
137 // Verify that only the mods can lock
140 local_user_view.person.id,
141 orig_post.community_id,
146 let post_id = data.post_id;
147 let locked = data.locked;
148 let updated_post = blocking(context.pool(), move |conn| {
149 Post::update_locked(conn, post_id, locked)
154 let form = ModLockPostForm {
155 mod_person_id: local_user_view.person.id,
156 post_id: data.post_id,
157 locked: Some(locked),
159 blocking(context.pool(), move |conn| ModLockPost::create(conn, &form)).await??;
162 CreateOrUpdatePost::send(
164 &local_user_view.person,
165 CreateOrUpdateType::Update,
170 send_post_ws_message(
172 UserOperation::LockPost,
174 Some(local_user_view.person.id),
181 #[async_trait::async_trait(?Send)]
182 impl Perform for StickyPost {
183 type Response = PostResponse;
187 context: &Data<LemmyContext>,
188 websocket_id: Option<ConnectionId>,
189 ) -> Result<PostResponse, LemmyError> {
190 let data: &StickyPost = self;
191 let local_user_view =
192 get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
194 let post_id = data.post_id;
195 let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
198 local_user_view.person.id,
199 orig_post.community_id,
204 // Verify that only the mods can sticky
207 local_user_view.person.id,
208 orig_post.community_id,
213 let post_id = data.post_id;
214 let stickied = data.stickied;
215 let updated_post = blocking(context.pool(), move |conn| {
216 Post::update_stickied(conn, post_id, stickied)
221 let form = ModStickyPostForm {
222 mod_person_id: local_user_view.person.id,
223 post_id: data.post_id,
224 stickied: Some(stickied),
226 blocking(context.pool(), move |conn| {
227 ModStickyPost::create(conn, &form)
232 // TODO stickied should pry work like locked for ease of use
233 CreateOrUpdatePost::send(
235 &local_user_view.person,
236 CreateOrUpdateType::Update,
241 send_post_ws_message(
243 UserOperation::StickyPost,
245 Some(local_user_view.person.id),
252 #[async_trait::async_trait(?Send)]
253 impl Perform for SavePost {
254 type Response = PostResponse;
258 context: &Data<LemmyContext>,
259 _websocket_id: Option<ConnectionId>,
260 ) -> Result<PostResponse, LemmyError> {
261 let data: &SavePost = self;
262 let local_user_view =
263 get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
265 let post_saved_form = PostSavedForm {
266 post_id: data.post_id,
267 person_id: local_user_view.person.id,
271 let save = move |conn: &'_ _| PostSaved::save(conn, &post_saved_form);
272 if blocking(context.pool(), save).await?.is_err() {
273 return Err(ApiError::err("couldnt_save_post").into());
276 let unsave = move |conn: &'_ _| PostSaved::unsave(conn, &post_saved_form);
277 if blocking(context.pool(), unsave).await?.is_err() {
278 return Err(ApiError::err("couldnt_save_post").into());
282 let post_id = data.post_id;
283 let person_id = local_user_view.person.id;
284 let post_view = blocking(context.pool(), move |conn| {
285 PostView::read(conn, post_id, Some(person_id))
289 // Mark the post as read
290 mark_post_as_read(person_id, post_id, context.pool()).await?;
292 Ok(PostResponse { post_view })
296 #[async_trait::async_trait(?Send)]
297 impl Perform for GetSiteMetadata {
298 type Response = GetSiteMetadataResponse;
302 context: &Data<LemmyContext>,
303 _websocket_id: Option<ConnectionId>,
304 ) -> Result<GetSiteMetadataResponse, LemmyError> {
305 let data: &Self = self;
307 let metadata = fetch_site_metadata(context.client(), &data.url).await?;
309 Ok(GetSiteMetadataResponse { metadata })