1 use std::convert::TryInto;
3 use actix_web::web::Data;
5 use lemmy_api_common::{
8 check_downvotes_enabled,
10 get_local_user_view_from_jwt,
13 fetcher::post_or_comment::PostOrComment,
14 protocol::activities::voting::{
16 vote::{Vote, VoteType},
19 use lemmy_db_schema::{
20 newtypes::LocalUserId,
22 traits::{Likeable, Saveable},
24 use lemmy_db_views::{comment_view::CommentView, local_user_view::LocalUserView};
25 use lemmy_utils::{ConnectionId, LemmyError};
26 use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperation};
30 #[async_trait::async_trait(?Send)]
31 impl Perform for MarkCommentAsRead {
32 type Response = CommentResponse;
34 #[tracing::instrument(skip(context, _websocket_id))]
37 context: &Data<LemmyContext>,
38 _websocket_id: Option<ConnectionId>,
39 ) -> Result<CommentResponse, LemmyError> {
40 let data: &MarkCommentAsRead = self;
42 get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
44 let comment_id = data.comment_id;
45 let orig_comment = blocking(context.pool(), move |conn| {
46 CommentView::read(conn, comment_id, None)
50 // Verify that only the recipient can mark as read
51 if local_user_view.person.id != orig_comment.get_recipient_id() {
52 return Err(LemmyError::from_message("no_comment_edit_allowed"));
55 // Do the mark as read
57 blocking(context.pool(), move |conn| {
58 Comment::update_read(conn, comment_id, read)
61 .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?;
64 let comment_id = data.comment_id;
65 let person_id = local_user_view.person.id;
66 let comment_view = blocking(context.pool(), move |conn| {
67 CommentView::read(conn, comment_id, Some(person_id))
71 let res = CommentResponse {
73 recipient_ids: Vec::new(),
81 #[async_trait::async_trait(?Send)]
82 impl Perform for SaveComment {
83 type Response = CommentResponse;
85 #[tracing::instrument(skip(context, _websocket_id))]
88 context: &Data<LemmyContext>,
89 _websocket_id: Option<ConnectionId>,
90 ) -> Result<CommentResponse, LemmyError> {
91 let data: &SaveComment = self;
93 get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
95 let comment_saved_form = CommentSavedForm {
96 comment_id: data.comment_id,
97 person_id: local_user_view.person.id,
101 let save_comment = move |conn: &'_ _| CommentSaved::save(conn, &comment_saved_form);
102 blocking(context.pool(), save_comment)
104 .map_err(|e| LemmyError::from_error_message(e, "couldnt_save_comment"))?;
106 let unsave_comment = move |conn: &'_ _| CommentSaved::unsave(conn, &comment_saved_form);
107 blocking(context.pool(), unsave_comment)
109 .map_err(|e| LemmyError::from_error_message(e, "couldnt_save_comment"))?;
112 let comment_id = data.comment_id;
113 let person_id = local_user_view.person.id;
114 let comment_view = blocking(context.pool(), move |conn| {
115 CommentView::read(conn, comment_id, Some(person_id))
121 recipient_ids: Vec::new(),
127 #[async_trait::async_trait(?Send)]
128 impl Perform for CreateCommentLike {
129 type Response = CommentResponse;
131 #[tracing::instrument(skip(context, websocket_id))]
134 context: &Data<LemmyContext>,
135 websocket_id: Option<ConnectionId>,
136 ) -> Result<CommentResponse, LemmyError> {
137 let data: &CreateCommentLike = self;
138 let local_user_view =
139 get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
141 let mut recipient_ids = Vec::<LocalUserId>::new();
143 // Don't do a downvote if site has downvotes disabled
144 check_downvotes_enabled(data.score, context.pool()).await?;
146 let comment_id = data.comment_id;
147 let orig_comment = blocking(context.pool(), move |conn| {
148 CommentView::read(conn, comment_id, None)
153 local_user_view.person.id,
154 orig_comment.community.id,
159 // Add parent user to recipients
160 let recipient_id = orig_comment.get_recipient_id();
161 if let Ok(local_recipient) = blocking(context.pool(), move |conn| {
162 LocalUserView::read_person(conn, recipient_id)
166 recipient_ids.push(local_recipient.local_user.id);
169 let like_form = CommentLikeForm {
170 comment_id: data.comment_id,
171 post_id: orig_comment.post.id,
172 person_id: local_user_view.person.id,
176 // Remove any likes first
177 let person_id = local_user_view.person.id;
178 blocking(context.pool(), move |conn| {
179 CommentLike::remove(conn, person_id, comment_id)
183 // Only add the like if the score isnt 0
184 let comment = orig_comment.comment;
185 let object = PostOrComment::Comment(Box::new(comment.into()));
186 let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
188 let like_form2 = like_form.clone();
189 let like = move |conn: &'_ _| CommentLike::like(conn, &like_form2);
190 blocking(context.pool(), like)
192 .map_err(|e| LemmyError::from_error_message(e, "couldnt_like_comment"))?;
196 &local_user_view.person.clone().into(),
197 orig_comment.community.id,
198 like_form.score.try_into()?,
203 // API doesn't distinguish between Undo/Like and Undo/Dislike
206 &local_user_view.person.clone().into(),
207 orig_comment.community.id,
214 send_comment_ws_message(
216 UserOperation::CreateCommentLike,
219 Some(local_user_view.person.id),