]> Untitled Git - lemmy.git/blob - crates/websocket/src/send.rs
First pass at invite-only migration. (#1949)
[lemmy.git] / crates / websocket / src / send.rs
1 use crate::{
2   messages::{SendComment, SendCommunityRoomMessage, SendPost, SendUserRoomMessage},
3   LemmyContext,
4   OperationType,
5 };
6 use lemmy_api_common::{
7   blocking,
8   check_person_block,
9   comment::CommentResponse,
10   community::CommunityResponse,
11   person::PrivateMessageResponse,
12   post::PostResponse,
13   send_email_to_user,
14 };
15 use lemmy_db_schema::{
16   newtypes::{CommentId, CommunityId, LocalUserId, PersonId, PostId, PrivateMessageId},
17   source::{
18     comment::Comment,
19     person::Person,
20     person_mention::{PersonMention, PersonMentionForm},
21     post::Post,
22   },
23   traits::{Crud, DeleteableOrRemoveable},
24 };
25 use lemmy_db_views::{
26   comment_view::CommentView,
27   local_user_view::LocalUserView,
28   post_view::PostView,
29   private_message_view::PrivateMessageView,
30 };
31 use lemmy_db_views_actor::community_view::CommunityView;
32 use lemmy_utils::{utils::MentionData, ConnectionId, LemmyError};
33
34 pub async fn send_post_ws_message<OP: ToString + Send + OperationType + 'static>(
35   post_id: PostId,
36   op: OP,
37   websocket_id: Option<ConnectionId>,
38   person_id: Option<PersonId>,
39   context: &LemmyContext,
40 ) -> Result<PostResponse, LemmyError> {
41   let post_view = blocking(context.pool(), move |conn| {
42     PostView::read(conn, post_id, person_id)
43   })
44   .await??;
45
46   let res = PostResponse { post_view };
47
48   context.chat_server().do_send(SendPost {
49     op,
50     post: res.clone(),
51     websocket_id,
52   });
53
54   Ok(res)
55 }
56
57 // TODO: in many call sites in apub crate, we are setting an empty vec for recipient_ids,
58 //       we should get the actual recipient actors from somewhere
59 pub async fn send_comment_ws_message_simple<OP: ToString + Send + OperationType + 'static>(
60   comment_id: CommentId,
61   op: OP,
62   context: &LemmyContext,
63 ) -> Result<CommentResponse, LemmyError> {
64   send_comment_ws_message(comment_id, op, None, None, None, vec![], context).await
65 }
66
67 pub async fn send_comment_ws_message<OP: ToString + Send + OperationType + 'static>(
68   comment_id: CommentId,
69   op: OP,
70   websocket_id: Option<ConnectionId>,
71   form_id: Option<String>,
72   person_id: Option<PersonId>,
73   recipient_ids: Vec<LocalUserId>,
74   context: &LemmyContext,
75 ) -> Result<CommentResponse, LemmyError> {
76   let mut view = blocking(context.pool(), move |conn| {
77     CommentView::read(conn, comment_id, person_id)
78   })
79   .await??;
80
81   if view.comment.deleted || view.comment.removed {
82     view.comment = view.comment.blank_out_deleted_or_removed_info();
83   }
84
85   let mut res = CommentResponse {
86     comment_view: view,
87     recipient_ids,
88     // The sent out form id should be null
89     form_id: None,
90   };
91
92   context.chat_server().do_send(SendComment {
93     op,
94     comment: res.clone(),
95     websocket_id,
96   });
97
98   // The recipient_ids should be empty for returns
99   res.recipient_ids = Vec::new();
100   res.form_id = form_id;
101
102   Ok(res)
103 }
104
105 pub async fn send_community_ws_message<OP: ToString + Send + OperationType + 'static>(
106   community_id: CommunityId,
107   op: OP,
108   websocket_id: Option<ConnectionId>,
109   person_id: Option<PersonId>,
110   context: &LemmyContext,
111 ) -> Result<CommunityResponse, LemmyError> {
112   let community_view = blocking(context.pool(), move |conn| {
113     CommunityView::read(conn, community_id, person_id)
114   })
115   .await??;
116
117   let res = CommunityResponse { community_view };
118
119   // Strip out the person id and subscribed when sending to others
120   let mut res_mut = res.clone();
121   res_mut.community_view.subscribed = false;
122
123   context.chat_server().do_send(SendCommunityRoomMessage {
124     op,
125     response: res_mut,
126     community_id: res.community_view.community.id,
127     websocket_id,
128   });
129
130   Ok(res)
131 }
132
133 pub async fn send_pm_ws_message<OP: ToString + Send + OperationType + 'static>(
134   private_message_id: PrivateMessageId,
135   op: OP,
136   websocket_id: Option<ConnectionId>,
137   context: &LemmyContext,
138 ) -> Result<PrivateMessageResponse, LemmyError> {
139   let mut view = blocking(context.pool(), move |conn| {
140     PrivateMessageView::read(conn, private_message_id)
141   })
142   .await??;
143
144   // Blank out deleted or removed info
145   if view.private_message.deleted {
146     view.private_message = view.private_message.blank_out_deleted_or_removed_info();
147   }
148
149   let res = PrivateMessageResponse {
150     private_message_view: view,
151   };
152
153   // Send notifications to the local recipient, if one exists
154   if res.private_message_view.recipient.local {
155     let recipient_id = res.private_message_view.recipient.id;
156     let local_recipient = blocking(context.pool(), move |conn| {
157       LocalUserView::read_person(conn, recipient_id)
158     })
159     .await??;
160     context.chat_server().do_send(SendUserRoomMessage {
161       op,
162       response: res.clone(),
163       local_recipient_id: local_recipient.local_user.id,
164       websocket_id,
165     });
166   }
167
168   Ok(res)
169 }
170
171 pub async fn send_local_notifs(
172   mentions: Vec<MentionData>,
173   comment: &Comment,
174   person: &Person,
175   post: &Post,
176   do_send_email: bool,
177   context: &LemmyContext,
178 ) -> Result<Vec<LocalUserId>, LemmyError> {
179   let mut recipient_ids = Vec::new();
180
181   // Send the local mentions
182   for mention in mentions
183     .iter()
184     .filter(|m| m.is_local(&context.settings().hostname) && m.name.ne(&person.name))
185     .collect::<Vec<&MentionData>>()
186   {
187     let mention_name = mention.name.clone();
188     let user_view = blocking(context.pool(), move |conn| {
189       LocalUserView::read_from_name(conn, &mention_name)
190     })
191     .await?;
192     if let Ok(mention_user_view) = user_view {
193       // TODO
194       // At some point, make it so you can't tag the parent creator either
195       // This can cause two notifications, one for reply and the other for mention
196       recipient_ids.push(mention_user_view.local_user.id);
197
198       let user_mention_form = PersonMentionForm {
199         recipient_id: mention_user_view.person.id,
200         comment_id: comment.id,
201         read: None,
202       };
203
204       // Allow this to fail softly, since comment edits might re-update or replace it
205       // Let the uniqueness handle this fail
206       blocking(context.pool(), move |conn| {
207         PersonMention::create(conn, &user_mention_form)
208       })
209       .await?
210       .ok();
211
212       // Send an email to those local users that have notifications on
213       if do_send_email {
214         send_email_to_user(
215           &mention_user_view,
216           "Mentioned by",
217           "Person Mention",
218           &comment.content,
219           &context.settings(),
220         )
221       }
222     }
223   }
224
225   // Send notifs to the parent commenter / poster
226   match comment.parent_id {
227     Some(parent_id) => {
228       let parent_comment =
229         blocking(context.pool(), move |conn| Comment::read(conn, parent_id)).await?;
230       if let Ok(parent_comment) = parent_comment {
231         // Get the parent commenter local_user
232         let parent_creator_id = parent_comment.creator_id;
233
234         // Only add to recipients if that person isn't blocked
235         let creator_blocked = check_person_block(person.id, parent_creator_id, context.pool())
236           .await
237           .is_err();
238
239         // Don't send a notif to yourself
240         if parent_comment.creator_id != person.id && !creator_blocked {
241           let user_view = blocking(context.pool(), move |conn| {
242             LocalUserView::read_person(conn, parent_creator_id)
243           })
244           .await?;
245           if let Ok(parent_user_view) = user_view {
246             recipient_ids.push(parent_user_view.local_user.id);
247
248             if do_send_email {
249               send_email_to_user(
250                 &parent_user_view,
251                 "Reply from",
252                 "Comment Reply",
253                 &comment.content,
254                 &context.settings(),
255               )
256             }
257           }
258         }
259       }
260     }
261     // Its a post
262     // Don't send a notif to yourself
263     None => {
264       // Only add to recipients if that person isn't blocked
265       let creator_blocked = check_person_block(person.id, post.creator_id, context.pool())
266         .await
267         .is_err();
268
269       if post.creator_id != person.id && !creator_blocked {
270         let creator_id = post.creator_id;
271         let parent_user = blocking(context.pool(), move |conn| {
272           LocalUserView::read_person(conn, creator_id)
273         })
274         .await?;
275         if let Ok(parent_user_view) = parent_user {
276           recipient_ids.push(parent_user_view.local_user.id);
277
278           if do_send_email {
279             send_email_to_user(
280               &parent_user_view,
281               "Reply from",
282               "Post Reply",
283               &comment.content,
284               &context.settings(),
285             )
286           }
287         }
288       }
289     }
290   };
291   Ok(recipient_ids)
292 }