]> Untitled Git - lemmy.git/blob - crates/api_crud/src/comment/create.rs
Fix clippy warnings added in nightly (#1833)
[lemmy.git] / crates / api_crud / src / comment / create.rs
1 use crate::PerformCrud;
2 use actix_web::web::Data;
3 use lemmy_api_common::{
4   blocking,
5   check_community_ban,
6   check_person_block,
7   comment::*,
8   get_local_user_view_from_jwt,
9   get_post,
10   send_local_notifs,
11 };
12 use lemmy_apub::{
13   activities::{
14     comment::create_or_update::CreateOrUpdateComment,
15     voting::vote::{Vote, VoteType},
16     CreateOrUpdateType,
17   },
18   fetcher::post_or_comment::PostOrComment,
19   generate_apub_endpoint,
20   EndpointType,
21 };
22 use lemmy_db_queries::{
23   source::{comment::Comment_, person_mention::PersonMention_},
24   Crud,
25   Likeable,
26 };
27 use lemmy_db_schema::source::{comment::*, person_mention::PersonMention};
28 use lemmy_db_views::comment_view::CommentView;
29 use lemmy_utils::{
30   utils::{remove_slurs, scrape_text_for_mentions},
31   ApiError,
32   ConnectionId,
33   LemmyError,
34 };
35 use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperationCrud};
36
37 #[async_trait::async_trait(?Send)]
38 impl PerformCrud for CreateComment {
39   type Response = CommentResponse;
40
41   async fn perform(
42     &self,
43     context: &Data<LemmyContext>,
44     websocket_id: Option<ConnectionId>,
45   ) -> Result<CommentResponse, LemmyError> {
46     let data: &CreateComment = self;
47     let local_user_view =
48       get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
49
50     let content_slurs_removed =
51       remove_slurs(&data.content.to_owned(), &context.settings().slur_regex());
52
53     // Check for a community ban
54     let post_id = data.post_id;
55     let post = get_post(post_id, context.pool()).await?;
56     let community_id = post.community_id;
57
58     check_community_ban(local_user_view.person.id, community_id, context.pool()).await?;
59
60     check_person_block(local_user_view.person.id, post.creator_id, context.pool()).await?;
61
62     // Check if post is locked, no new comments
63     if post.locked {
64       return Err(ApiError::err("locked").into());
65     }
66
67     // If there's a parent_id, check to make sure that comment is in that post
68     if let Some(parent_id) = data.parent_id {
69       // Make sure the parent comment exists
70       let parent = blocking(context.pool(), move |conn| Comment::read(conn, parent_id))
71         .await?
72         .map_err(|_| ApiError::err("couldnt_create_comment"))?;
73
74       check_person_block(local_user_view.person.id, parent.creator_id, context.pool()).await?;
75
76       // Strange issue where sometimes the post ID is incorrect
77       if parent.post_id != post_id {
78         return Err(ApiError::err("couldnt_create_comment").into());
79       }
80     }
81
82     let comment_form = CommentForm {
83       content: content_slurs_removed,
84       parent_id: data.parent_id.to_owned(),
85       post_id: data.post_id,
86       creator_id: local_user_view.person.id,
87       ..CommentForm::default()
88     };
89
90     // Create the comment
91     let comment_form2 = comment_form.clone();
92     let inserted_comment = blocking(context.pool(), move |conn| {
93       Comment::create(conn, &comment_form2)
94     })
95     .await?
96     .map_err(|_| ApiError::err("couldnt_create_comment"))?;
97
98     // Necessary to update the ap_id
99     let inserted_comment_id = inserted_comment.id;
100     let protocol_and_hostname = context.settings().get_protocol_and_hostname();
101
102     let updated_comment: Comment =
103       blocking(context.pool(), move |conn| -> Result<Comment, LemmyError> {
104         let apub_id = generate_apub_endpoint(
105           EndpointType::Comment,
106           &inserted_comment_id.to_string(),
107           &protocol_and_hostname,
108         )?;
109         Ok(Comment::update_ap_id(conn, inserted_comment_id, apub_id)?)
110       })
111       .await?
112       .map_err(|_| ApiError::err("couldnt_create_comment"))?;
113
114     CreateOrUpdateComment::send(
115       &updated_comment,
116       &local_user_view.person,
117       CreateOrUpdateType::Create,
118       context,
119     )
120     .await?;
121
122     // Scan the comment for user mentions, add those rows
123     let post_id = post.id;
124     let mentions = scrape_text_for_mentions(&comment_form.content);
125     let recipient_ids = send_local_notifs(
126       mentions,
127       updated_comment.clone(),
128       local_user_view.person.clone(),
129       post,
130       context.pool(),
131       true,
132       &context.settings(),
133     )
134     .await?;
135
136     // You like your own comment by default
137     let like_form = CommentLikeForm {
138       comment_id: inserted_comment.id,
139       post_id,
140       person_id: local_user_view.person.id,
141       score: 1,
142     };
143
144     let like = move |conn: &'_ _| CommentLike::like(conn, &like_form);
145     if blocking(context.pool(), like).await?.is_err() {
146       return Err(ApiError::err("couldnt_like_comment").into());
147     }
148
149     let object = PostOrComment::Comment(updated_comment);
150     Vote::send(
151       &object,
152       &local_user_view.person,
153       community_id,
154       VoteType::Like,
155       context,
156     )
157     .await?;
158
159     let person_id = local_user_view.person.id;
160     let comment_id = inserted_comment.id;
161     let comment_view = blocking(context.pool(), move |conn| {
162       CommentView::read(conn, comment_id, Some(person_id))
163     })
164     .await??;
165
166     // If its a comment to yourself, mark it as read
167     if local_user_view.person.id == comment_view.get_recipient_id() {
168       let comment_id = inserted_comment.id;
169       blocking(context.pool(), move |conn| {
170         Comment::update_read(conn, comment_id, true)
171       })
172       .await?
173       .map_err(|_| ApiError::err("couldnt_update_comment"))?;
174     }
175     // If its a reply, mark the parent as read
176     if let Some(parent_id) = data.parent_id {
177       let parent_comment = blocking(context.pool(), move |conn| {
178         CommentView::read(conn, parent_id, Some(person_id))
179       })
180       .await??;
181       if local_user_view.person.id == parent_comment.get_recipient_id() {
182         blocking(context.pool(), move |conn| {
183           Comment::update_read(conn, parent_id, true)
184         })
185         .await?
186         .map_err(|_| ApiError::err("couldnt_update_parent_comment"))?;
187       }
188       // If the parent has PersonMentions mark them as read too
189       let person_id = local_user_view.person.id;
190       let person_mention = blocking(context.pool(), move |conn| {
191         PersonMention::read_by_comment_and_person(conn, parent_id, person_id)
192       })
193       .await?;
194       if let Ok(mention) = person_mention {
195         blocking(context.pool(), move |conn| {
196           PersonMention::update_read(conn, mention.id, true)
197         })
198         .await?
199         .map_err(|_| ApiError::err("couldnt_update_person_mentions"))?;
200       }
201     }
202
203     send_comment_ws_message(
204       inserted_comment.id,
205       UserOperationCrud::CreateComment,
206       websocket_id,
207       data.form_id.to_owned(),
208       Some(local_user_view.person.id),
209       recipient_ids,
210       context,
211     )
212     .await
213   }
214 }