1 use std::convert::TryInto;
3 use actix_web::web::Data;
5 use lemmy_api_common::{
8 check_downvotes_enabled,
11 get_local_user_view_from_jwt,
14 fetcher::post_or_comment::PostOrComment,
15 protocol::activities::voting::{
17 vote::{Vote, VoteType},
20 use lemmy_db_schema::{
21 newtypes::LocalUserId,
23 traits::{Likeable, Saveable},
25 use lemmy_db_views::{comment_view::CommentView, local_user_view::LocalUserView};
26 use lemmy_utils::{ConnectionId, LemmyError};
27 use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperation};
31 #[async_trait::async_trait(?Send)]
32 impl Perform for MarkCommentAsRead {
33 type Response = CommentResponse;
35 #[tracing::instrument(skip(context, _websocket_id))]
38 context: &Data<LemmyContext>,
39 _websocket_id: Option<ConnectionId>,
40 ) -> Result<CommentResponse, LemmyError> {
41 let data: &MarkCommentAsRead = self;
43 get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
45 let comment_id = data.comment_id;
46 let orig_comment = blocking(context.pool(), move |conn| {
47 CommentView::read(conn, comment_id, None)
52 local_user_view.person.id,
53 orig_comment.community.id,
58 // Verify that only the recipient can mark as read
59 if local_user_view.person.id != orig_comment.get_recipient_id() {
60 return Err(LemmyError::from_message("no_comment_edit_allowed"));
63 // Do the mark as read
65 blocking(context.pool(), move |conn| {
66 Comment::update_read(conn, comment_id, read)
69 .map_err(LemmyError::from)
70 .map_err(|e| e.with_message("couldnt_update_comment"))?;
73 let comment_id = data.comment_id;
74 let person_id = local_user_view.person.id;
75 let comment_view = blocking(context.pool(), move |conn| {
76 CommentView::read(conn, comment_id, Some(person_id))
80 let res = CommentResponse {
82 recipient_ids: Vec::new(),
90 #[async_trait::async_trait(?Send)]
91 impl Perform for SaveComment {
92 type Response = CommentResponse;
94 #[tracing::instrument(skip(context, _websocket_id))]
97 context: &Data<LemmyContext>,
98 _websocket_id: Option<ConnectionId>,
99 ) -> Result<CommentResponse, LemmyError> {
100 let data: &SaveComment = self;
101 let local_user_view =
102 get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
104 let comment_saved_form = CommentSavedForm {
105 comment_id: data.comment_id,
106 person_id: local_user_view.person.id,
110 let save_comment = move |conn: &'_ _| CommentSaved::save(conn, &comment_saved_form);
111 blocking(context.pool(), save_comment)
113 .map_err(LemmyError::from)
114 .map_err(|e| e.with_message("couldnt_save_comment"))?;
116 let unsave_comment = move |conn: &'_ _| CommentSaved::unsave(conn, &comment_saved_form);
117 blocking(context.pool(), unsave_comment)
119 .map_err(LemmyError::from)
120 .map_err(|e| e.with_message("couldnt_save_comment"))?;
123 let comment_id = data.comment_id;
124 let person_id = local_user_view.person.id;
125 let comment_view = blocking(context.pool(), move |conn| {
126 CommentView::read(conn, comment_id, Some(person_id))
132 recipient_ids: Vec::new(),
138 #[async_trait::async_trait(?Send)]
139 impl Perform for CreateCommentLike {
140 type Response = CommentResponse;
142 #[tracing::instrument(skip(context, websocket_id))]
145 context: &Data<LemmyContext>,
146 websocket_id: Option<ConnectionId>,
147 ) -> Result<CommentResponse, LemmyError> {
148 let data: &CreateCommentLike = self;
149 let local_user_view =
150 get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
152 let mut recipient_ids = Vec::<LocalUserId>::new();
154 // Don't do a downvote if site has downvotes disabled
155 check_downvotes_enabled(data.score, context.pool()).await?;
157 let comment_id = data.comment_id;
158 let orig_comment = blocking(context.pool(), move |conn| {
159 CommentView::read(conn, comment_id, None)
164 local_user_view.person.id,
165 orig_comment.community.id,
171 local_user_view.person.id,
172 orig_comment.get_recipient_id(),
177 // Add parent user to recipients
178 let recipient_id = orig_comment.get_recipient_id();
179 if let Ok(local_recipient) = blocking(context.pool(), move |conn| {
180 LocalUserView::read_person(conn, recipient_id)
184 recipient_ids.push(local_recipient.local_user.id);
187 let like_form = CommentLikeForm {
188 comment_id: data.comment_id,
189 post_id: orig_comment.post.id,
190 person_id: local_user_view.person.id,
194 // Remove any likes first
195 let person_id = local_user_view.person.id;
196 blocking(context.pool(), move |conn| {
197 CommentLike::remove(conn, person_id, comment_id)
201 // Only add the like if the score isnt 0
202 let comment = orig_comment.comment;
203 let object = PostOrComment::Comment(Box::new(comment.into()));
204 let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
206 let like_form2 = like_form.clone();
207 let like = move |conn: &'_ _| CommentLike::like(conn, &like_form2);
208 blocking(context.pool(), like)
210 .map_err(LemmyError::from)
211 .map_err(|e| e.with_message("couldnt_like_comment"))?;
215 &local_user_view.person.clone().into(),
216 orig_comment.community.id,
217 like_form.score.try_into()?,
222 // API doesn't distinguish between Undo/Like and Undo/Dislike
225 &local_user_view.person.clone().into(),
226 orig_comment.community.id,
233 send_comment_ws_message(
235 UserOperation::CreateCommentLike,
238 Some(local_user_view.person.id),