]> Untitled Git - lemmy.git/blob - crates/api/src/comment.rs
ff1010fb1bfb997c04f24e5cd0f4ec7337aa7a33
[lemmy.git] / crates / api / src / comment.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   comment::*,
8   get_local_user_view_from_jwt,
9 };
10 use lemmy_apub::ApubLikeableType;
11 use lemmy_db_queries::{source::comment::Comment_, Likeable, Saveable};
12 use lemmy_db_schema::{source::comment::*, LocalUserId};
13 use lemmy_db_views::{comment_view::CommentView, local_user_view::LocalUserView};
14 use lemmy_utils::{ApiError, ConnectionId, LemmyError};
15 use lemmy_websocket::{messages::SendComment, LemmyContext, UserOperation};
16
17 #[async_trait::async_trait(?Send)]
18 impl Perform for MarkCommentAsRead {
19   type Response = CommentResponse;
20
21   async fn perform(
22     &self,
23     context: &Data<LemmyContext>,
24     _websocket_id: Option<ConnectionId>,
25   ) -> Result<CommentResponse, LemmyError> {
26     let data: &MarkCommentAsRead = self;
27     let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
28
29     let comment_id = data.comment_id;
30     let orig_comment = blocking(context.pool(), move |conn| {
31       CommentView::read(conn, comment_id, None)
32     })
33     .await??;
34
35     check_community_ban(
36       local_user_view.person.id,
37       orig_comment.community.id,
38       context.pool(),
39     )
40     .await?;
41
42     // Verify that only the recipient can mark as read
43     if local_user_view.person.id != orig_comment.get_recipient_id() {
44       return Err(ApiError::err("no_comment_edit_allowed").into());
45     }
46
47     // Do the mark as read
48     let read = data.read;
49     blocking(context.pool(), move |conn| {
50       Comment::update_read(conn, comment_id, read)
51     })
52     .await?
53     .map_err(|_| ApiError::err("couldnt_update_comment"))?;
54
55     // Refetch it
56     let comment_id = data.comment_id;
57     let person_id = local_user_view.person.id;
58     let comment_view = blocking(context.pool(), move |conn| {
59       CommentView::read(conn, comment_id, Some(person_id))
60     })
61     .await??;
62
63     let res = CommentResponse {
64       comment_view,
65       recipient_ids: Vec::new(),
66       form_id: None,
67     };
68
69     Ok(res)
70   }
71 }
72
73 #[async_trait::async_trait(?Send)]
74 impl Perform for SaveComment {
75   type Response = CommentResponse;
76
77   async fn perform(
78     &self,
79     context: &Data<LemmyContext>,
80     _websocket_id: Option<ConnectionId>,
81   ) -> Result<CommentResponse, LemmyError> {
82     let data: &SaveComment = self;
83     let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
84
85     let comment_saved_form = CommentSavedForm {
86       comment_id: data.comment_id,
87       person_id: local_user_view.person.id,
88     };
89
90     if data.save {
91       let save_comment = move |conn: &'_ _| CommentSaved::save(conn, &comment_saved_form);
92       if blocking(context.pool(), save_comment).await?.is_err() {
93         return Err(ApiError::err("couldnt_save_comment").into());
94       }
95     } else {
96       let unsave_comment = move |conn: &'_ _| CommentSaved::unsave(conn, &comment_saved_form);
97       if blocking(context.pool(), unsave_comment).await?.is_err() {
98         return Err(ApiError::err("couldnt_save_comment").into());
99       }
100     }
101
102     let comment_id = data.comment_id;
103     let person_id = local_user_view.person.id;
104     let comment_view = blocking(context.pool(), move |conn| {
105       CommentView::read(conn, comment_id, Some(person_id))
106     })
107     .await??;
108
109     Ok(CommentResponse {
110       comment_view,
111       recipient_ids: Vec::new(),
112       form_id: None,
113     })
114   }
115 }
116
117 #[async_trait::async_trait(?Send)]
118 impl Perform for CreateCommentLike {
119   type Response = CommentResponse;
120
121   async fn perform(
122     &self,
123     context: &Data<LemmyContext>,
124     websocket_id: Option<ConnectionId>,
125   ) -> Result<CommentResponse, LemmyError> {
126     let data: &CreateCommentLike = self;
127     let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
128
129     let mut recipient_ids = Vec::<LocalUserId>::new();
130
131     // Don't do a downvote if site has downvotes disabled
132     check_downvotes_enabled(data.score, context.pool()).await?;
133
134     let comment_id = data.comment_id;
135     let orig_comment = blocking(context.pool(), move |conn| {
136       CommentView::read(conn, comment_id, None)
137     })
138     .await??;
139
140     check_community_ban(
141       local_user_view.person.id,
142       orig_comment.community.id,
143       context.pool(),
144     )
145     .await?;
146
147     // Add parent user to recipients
148     let recipient_id = orig_comment.get_recipient_id();
149     if let Ok(local_recipient) = blocking(context.pool(), move |conn| {
150       LocalUserView::read_person(conn, recipient_id)
151     })
152     .await?
153     {
154       recipient_ids.push(local_recipient.local_user.id);
155     }
156
157     let like_form = CommentLikeForm {
158       comment_id: data.comment_id,
159       post_id: orig_comment.post.id,
160       person_id: local_user_view.person.id,
161       score: data.score,
162     };
163
164     // Remove any likes first
165     let person_id = local_user_view.person.id;
166     blocking(context.pool(), move |conn| {
167       CommentLike::remove(conn, person_id, comment_id)
168     })
169     .await??;
170
171     // Only add the like if the score isnt 0
172     let comment = orig_comment.comment;
173     let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
174     if do_add {
175       let like_form2 = like_form.clone();
176       let like = move |conn: &'_ _| CommentLike::like(conn, &like_form2);
177       if blocking(context.pool(), like).await?.is_err() {
178         return Err(ApiError::err("couldnt_like_comment").into());
179       }
180
181       if like_form.score == 1 {
182         comment.send_like(&local_user_view.person, context).await?;
183       } else if like_form.score == -1 {
184         comment
185           .send_dislike(&local_user_view.person, context)
186           .await?;
187       }
188     } else {
189       comment
190         .send_undo_like(&local_user_view.person, context)
191         .await?;
192     }
193
194     // Have to refetch the comment to get the current state
195     let comment_id = data.comment_id;
196     let person_id = local_user_view.person.id;
197     let liked_comment = blocking(context.pool(), move |conn| {
198       CommentView::read(conn, comment_id, Some(person_id))
199     })
200     .await??;
201
202     let res = CommentResponse {
203       comment_view: liked_comment,
204       recipient_ids,
205       form_id: None,
206     };
207
208     context.chat_server().do_send(SendComment {
209       op: UserOperation::CreateCommentLike,
210       comment: res.clone(),
211       websocket_id,
212     });
213
214     Ok(res)
215   }
216 }