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