2 use actix_web::web::Data;
3 use lemmy_api_common::{
6 check_downvotes_enabled,
8 get_local_user_view_from_jwt,
13 vote::{Vote, VoteType},
17 use lemmy_db_queries::{source::comment::Comment_, Likeable, Saveable};
18 use lemmy_db_schema::{source::comment::*, LocalUserId};
19 use lemmy_db_views::{comment_view::CommentView, local_user_view::LocalUserView};
20 use lemmy_utils::{ApiError, ConnectionId, LemmyError};
21 use lemmy_websocket::{messages::SendComment, LemmyContext, UserOperation};
22 use std::convert::TryInto;
24 #[async_trait::async_trait(?Send)]
25 impl Perform for MarkCommentAsRead {
26 type Response = CommentResponse;
30 context: &Data<LemmyContext>,
31 _websocket_id: Option<ConnectionId>,
32 ) -> Result<CommentResponse, LemmyError> {
33 let data: &MarkCommentAsRead = self;
34 let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
36 let comment_id = data.comment_id;
37 let orig_comment = blocking(context.pool(), move |conn| {
38 CommentView::read(conn, comment_id, None)
43 local_user_view.person.id,
44 orig_comment.community.id,
49 // Verify that only the recipient can mark as read
50 if local_user_view.person.id != orig_comment.get_recipient_id() {
51 return Err(ApiError::err("no_comment_edit_allowed").into());
54 // Do the mark as read
56 blocking(context.pool(), move |conn| {
57 Comment::update_read(conn, comment_id, read)
60 .map_err(|_| ApiError::err("couldnt_update_comment"))?;
63 let comment_id = data.comment_id;
64 let person_id = local_user_view.person.id;
65 let comment_view = blocking(context.pool(), move |conn| {
66 CommentView::read(conn, comment_id, Some(person_id))
70 let res = CommentResponse {
72 recipient_ids: Vec::new(),
80 #[async_trait::async_trait(?Send)]
81 impl Perform for SaveComment {
82 type Response = CommentResponse;
86 context: &Data<LemmyContext>,
87 _websocket_id: Option<ConnectionId>,
88 ) -> Result<CommentResponse, LemmyError> {
89 let data: &SaveComment = self;
90 let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
92 let comment_saved_form = CommentSavedForm {
93 comment_id: data.comment_id,
94 person_id: local_user_view.person.id,
98 let save_comment = move |conn: &'_ _| CommentSaved::save(conn, &comment_saved_form);
99 if blocking(context.pool(), save_comment).await?.is_err() {
100 return Err(ApiError::err("couldnt_save_comment").into());
103 let unsave_comment = move |conn: &'_ _| CommentSaved::unsave(conn, &comment_saved_form);
104 if blocking(context.pool(), unsave_comment).await?.is_err() {
105 return Err(ApiError::err("couldnt_save_comment").into());
109 let comment_id = data.comment_id;
110 let person_id = local_user_view.person.id;
111 let comment_view = blocking(context.pool(), move |conn| {
112 CommentView::read(conn, comment_id, Some(person_id))
118 recipient_ids: Vec::new(),
124 #[async_trait::async_trait(?Send)]
125 impl Perform for CreateCommentLike {
126 type Response = CommentResponse;
130 context: &Data<LemmyContext>,
131 websocket_id: Option<ConnectionId>,
132 ) -> Result<CommentResponse, LemmyError> {
133 let data: &CreateCommentLike = self;
134 let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
136 let mut recipient_ids = Vec::<LocalUserId>::new();
138 // Don't do a downvote if site has downvotes disabled
139 check_downvotes_enabled(data.score, context.pool()).await?;
141 let comment_id = data.comment_id;
142 let orig_comment = blocking(context.pool(), move |conn| {
143 CommentView::read(conn, comment_id, None)
148 local_user_view.person.id,
149 orig_comment.community.id,
154 // Add parent user to recipients
155 let recipient_id = orig_comment.get_recipient_id();
156 if let Ok(local_recipient) = blocking(context.pool(), move |conn| {
157 LocalUserView::read_person(conn, recipient_id)
161 recipient_ids.push(local_recipient.local_user.id);
164 let like_form = CommentLikeForm {
165 comment_id: data.comment_id,
166 post_id: orig_comment.post.id,
167 person_id: local_user_view.person.id,
171 // Remove any likes first
172 let person_id = local_user_view.person.id;
173 blocking(context.pool(), move |conn| {
174 CommentLike::remove(conn, person_id, comment_id)
178 // Only add the like if the score isnt 0
179 let comment = orig_comment.comment;
180 let object = PostOrComment::Comment(Box::new(comment));
181 let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
183 let like_form2 = like_form.clone();
184 let like = move |conn: &'_ _| CommentLike::like(conn, &like_form2);
185 if blocking(context.pool(), like).await?.is_err() {
186 return Err(ApiError::err("couldnt_like_comment").into());
191 &local_user_view.person,
192 orig_comment.community.id,
193 like_form.score.try_into()?,
198 // API doesn't distinguish between Undo/Like and Undo/Dislike
201 &local_user_view.person,
202 orig_comment.community.id,
209 // Have to refetch the comment to get the current state
210 let comment_id = data.comment_id;
211 let person_id = local_user_view.person.id;
212 let liked_comment = blocking(context.pool(), move |conn| {
213 CommentView::read(conn, comment_id, Some(person_id))
217 let res = CommentResponse {
218 comment_view: liked_comment,
223 context.chat_server().do_send(SendComment {
224 op: UserOperation::CreateCommentLike,
225 comment: res.clone(),