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