]> Untitled Git - lemmy.git/blob - crates/db_views_actor/src/person_mention_view.rs
0be446a919ff8530de76b369656965d50b35baa8
[lemmy.git] / crates / db_views_actor / src / person_mention_view.rs
1 use diesel::{dsl::*, result::Error, *};
2 use lemmy_db_schema::{
3   aggregates::comment_aggregates::CommentAggregates,
4   functions::hot_rank,
5   limit_and_offset,
6   newtypes::{PersonId, PersonMentionId},
7   schema::{
8     comment,
9     comment_aggregates,
10     comment_like,
11     comment_saved,
12     community,
13     community_follower,
14     community_person_ban,
15     person,
16     person_alias_1,
17     person_block,
18     person_mention,
19     post,
20   },
21   source::{
22     comment::{Comment, CommentSaved},
23     community::{Community, CommunityFollower, CommunityPersonBan, CommunitySafe},
24     person::{Person, PersonAlias1, PersonSafe, PersonSafeAlias1},
25     person_block::PersonBlock,
26     person_mention::PersonMention,
27     post::Post,
28   },
29   traits::{MaybeOptional, ToSafe, ViewToVec},
30   SortType,
31 };
32 use serde::{Deserialize, Serialize};
33
34 #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
35 pub struct PersonMentionView {
36   pub person_mention: PersonMention,
37   pub comment: Comment,
38   pub creator: PersonSafe,
39   pub post: Post,
40   pub community: CommunitySafe,
41   pub recipient: PersonSafeAlias1,
42   pub counts: CommentAggregates,
43   pub creator_banned_from_community: bool, // Left Join to CommunityPersonBan
44   pub subscribed: bool,                    // Left join to CommunityFollower
45   pub saved: bool,                         // Left join to CommentSaved
46   pub creator_blocked: bool,               // Left join to PersonBlock
47   pub my_vote: Option<i16>,                // Left join to CommentLike
48 }
49
50 type PersonMentionViewTuple = (
51   PersonMention,
52   Comment,
53   PersonSafe,
54   Post,
55   CommunitySafe,
56   PersonSafeAlias1,
57   CommentAggregates,
58   Option<CommunityPersonBan>,
59   Option<CommunityFollower>,
60   Option<CommentSaved>,
61   Option<PersonBlock>,
62   Option<i16>,
63 );
64
65 impl PersonMentionView {
66   pub fn read(
67     conn: &PgConnection,
68     person_mention_id: PersonMentionId,
69     my_person_id: Option<PersonId>,
70   ) -> Result<Self, Error> {
71     // The left join below will return None in this case
72     let person_id_join = my_person_id.unwrap_or(PersonId(-1));
73
74     let (
75       person_mention,
76       comment,
77       creator,
78       post,
79       community,
80       recipient,
81       counts,
82       creator_banned_from_community,
83       subscribed,
84       saved,
85       creator_blocked,
86       my_vote,
87     ) = person_mention::table
88       .find(person_mention_id)
89       .inner_join(comment::table)
90       .inner_join(person::table.on(comment::creator_id.eq(person::id)))
91       .inner_join(post::table.on(comment::post_id.eq(post::id)))
92       .inner_join(community::table.on(post::community_id.eq(community::id)))
93       .inner_join(person_alias_1::table)
94       .inner_join(comment_aggregates::table.on(comment::id.eq(comment_aggregates::comment_id)))
95       .left_join(
96         community_person_ban::table.on(
97           community::id
98             .eq(community_person_ban::community_id)
99             .and(community_person_ban::person_id.eq(comment::creator_id))
100             .and(
101               community_person_ban::expires
102                 .is_null()
103                 .or(community_person_ban::expires.gt(now)),
104             ),
105         ),
106       )
107       .left_join(
108         community_follower::table.on(
109           post::community_id
110             .eq(community_follower::community_id)
111             .and(community_follower::person_id.eq(person_id_join)),
112         ),
113       )
114       .left_join(
115         comment_saved::table.on(
116           comment::id
117             .eq(comment_saved::comment_id)
118             .and(comment_saved::person_id.eq(person_id_join)),
119         ),
120       )
121       .left_join(
122         person_block::table.on(
123           comment::creator_id
124             .eq(person_block::target_id)
125             .and(person_block::person_id.eq(person_id_join)),
126         ),
127       )
128       .left_join(
129         comment_like::table.on(
130           comment::id
131             .eq(comment_like::comment_id)
132             .and(comment_like::person_id.eq(person_id_join)),
133         ),
134       )
135       .select((
136         person_mention::all_columns,
137         comment::all_columns,
138         Person::safe_columns_tuple(),
139         post::all_columns,
140         Community::safe_columns_tuple(),
141         PersonAlias1::safe_columns_tuple(),
142         comment_aggregates::all_columns,
143         community_person_ban::all_columns.nullable(),
144         community_follower::all_columns.nullable(),
145         comment_saved::all_columns.nullable(),
146         person_block::all_columns.nullable(),
147         comment_like::score.nullable(),
148       ))
149       .first::<PersonMentionViewTuple>(conn)?;
150
151     Ok(PersonMentionView {
152       person_mention,
153       comment,
154       creator,
155       post,
156       community,
157       recipient,
158       counts,
159       creator_banned_from_community: creator_banned_from_community.is_some(),
160       subscribed: subscribed.is_some(),
161       saved: saved.is_some(),
162       creator_blocked: creator_blocked.is_some(),
163       my_vote,
164     })
165   }
166
167   /// Gets the number of unread mentions
168   pub fn get_unread_mentions(conn: &PgConnection, my_person_id: PersonId) -> Result<i64, Error> {
169     use diesel::dsl::*;
170
171     person_mention::table
172       .filter(person_mention::recipient_id.eq(my_person_id))
173       .filter(person_mention::read.eq(false))
174       .select(count(person_mention::id))
175       .first::<i64>(conn)
176   }
177 }
178
179 pub struct PersonMentionQueryBuilder<'a> {
180   conn: &'a PgConnection,
181   my_person_id: Option<PersonId>,
182   recipient_id: Option<PersonId>,
183   sort: Option<SortType>,
184   unread_only: Option<bool>,
185   page: Option<i64>,
186   limit: Option<i64>,
187 }
188
189 impl<'a> PersonMentionQueryBuilder<'a> {
190   pub fn create(conn: &'a PgConnection) -> Self {
191     PersonMentionQueryBuilder {
192       conn,
193       my_person_id: None,
194       recipient_id: None,
195       sort: None,
196       unread_only: None,
197       page: None,
198       limit: None,
199     }
200   }
201
202   pub fn sort<T: MaybeOptional<SortType>>(mut self, sort: T) -> Self {
203     self.sort = sort.get_optional();
204     self
205   }
206
207   pub fn unread_only<T: MaybeOptional<bool>>(mut self, unread_only: T) -> Self {
208     self.unread_only = unread_only.get_optional();
209     self
210   }
211
212   pub fn recipient_id<T: MaybeOptional<PersonId>>(mut self, recipient_id: T) -> Self {
213     self.recipient_id = recipient_id.get_optional();
214     self
215   }
216
217   pub fn my_person_id<T: MaybeOptional<PersonId>>(mut self, my_person_id: T) -> Self {
218     self.my_person_id = my_person_id.get_optional();
219     self
220   }
221
222   pub fn page<T: MaybeOptional<i64>>(mut self, page: T) -> Self {
223     self.page = page.get_optional();
224     self
225   }
226
227   pub fn limit<T: MaybeOptional<i64>>(mut self, limit: T) -> Self {
228     self.limit = limit.get_optional();
229     self
230   }
231
232   pub fn list(self) -> Result<Vec<PersonMentionView>, Error> {
233     use diesel::dsl::*;
234
235     // The left join below will return None in this case
236     let person_id_join = self.my_person_id.unwrap_or(PersonId(-1));
237
238     let mut query = person_mention::table
239       .inner_join(comment::table)
240       .inner_join(person::table.on(comment::creator_id.eq(person::id)))
241       .inner_join(post::table.on(comment::post_id.eq(post::id)))
242       .inner_join(community::table.on(post::community_id.eq(community::id)))
243       .inner_join(person_alias_1::table)
244       .inner_join(comment_aggregates::table.on(comment::id.eq(comment_aggregates::comment_id)))
245       .left_join(
246         community_person_ban::table.on(
247           community::id
248             .eq(community_person_ban::community_id)
249             .and(community_person_ban::person_id.eq(comment::creator_id))
250             .and(
251               community_person_ban::expires
252                 .is_null()
253                 .or(community_person_ban::expires.gt(now)),
254             ),
255         ),
256       )
257       .left_join(
258         community_follower::table.on(
259           post::community_id
260             .eq(community_follower::community_id)
261             .and(community_follower::person_id.eq(person_id_join)),
262         ),
263       )
264       .left_join(
265         comment_saved::table.on(
266           comment::id
267             .eq(comment_saved::comment_id)
268             .and(comment_saved::person_id.eq(person_id_join)),
269         ),
270       )
271       .left_join(
272         person_block::table.on(
273           comment::creator_id
274             .eq(person_block::target_id)
275             .and(person_block::person_id.eq(person_id_join)),
276         ),
277       )
278       .left_join(
279         comment_like::table.on(
280           comment::id
281             .eq(comment_like::comment_id)
282             .and(comment_like::person_id.eq(person_id_join)),
283         ),
284       )
285       .select((
286         person_mention::all_columns,
287         comment::all_columns,
288         Person::safe_columns_tuple(),
289         post::all_columns,
290         Community::safe_columns_tuple(),
291         PersonAlias1::safe_columns_tuple(),
292         comment_aggregates::all_columns,
293         community_person_ban::all_columns.nullable(),
294         community_follower::all_columns.nullable(),
295         comment_saved::all_columns.nullable(),
296         person_block::all_columns.nullable(),
297         comment_like::score.nullable(),
298       ))
299       .into_boxed();
300
301     if let Some(recipient_id) = self.recipient_id {
302       query = query.filter(person_mention::recipient_id.eq(recipient_id));
303     }
304
305     if self.unread_only.unwrap_or(false) {
306       query = query.filter(person_mention::read.eq(false));
307     }
308
309     query = match self.sort.unwrap_or(SortType::Hot) {
310       SortType::Hot | SortType::Active => query
311         .order_by(hot_rank(comment_aggregates::score, comment_aggregates::published).desc())
312         .then_order_by(comment_aggregates::published.desc()),
313       SortType::New | SortType::MostComments | SortType::NewComments => {
314         query.order_by(comment::published.desc())
315       }
316       SortType::TopAll => query.order_by(comment_aggregates::score.desc()),
317       SortType::TopYear => query
318         .filter(comment::published.gt(now - 1.years()))
319         .order_by(comment_aggregates::score.desc()),
320       SortType::TopMonth => query
321         .filter(comment::published.gt(now - 1.months()))
322         .order_by(comment_aggregates::score.desc()),
323       SortType::TopWeek => query
324         .filter(comment::published.gt(now - 1.weeks()))
325         .order_by(comment_aggregates::score.desc()),
326       SortType::TopDay => query
327         .filter(comment::published.gt(now - 1.days()))
328         .order_by(comment_aggregates::score.desc()),
329     };
330
331     let (limit, offset) = limit_and_offset(self.page, self.limit);
332
333     let res = query
334       .limit(limit)
335       .offset(offset)
336       .load::<PersonMentionViewTuple>(self.conn)?;
337
338     Ok(PersonMentionView::from_tuple_to_vec(res))
339   }
340 }
341
342 impl ViewToVec for PersonMentionView {
343   type DbTuple = PersonMentionViewTuple;
344   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
345     items
346       .iter()
347       .map(|a| Self {
348         person_mention: a.0.to_owned(),
349         comment: a.1.to_owned(),
350         creator: a.2.to_owned(),
351         post: a.3.to_owned(),
352         community: a.4.to_owned(),
353         recipient: a.5.to_owned(),
354         counts: a.6.to_owned(),
355         creator_banned_from_community: a.7.is_some(),
356         subscribed: a.8.is_some(),
357         saved: a.9.is_some(),
358         creator_blocked: a.10.is_some(),
359         my_vote: a.11,
360       })
361       .collect::<Vec<Self>>()
362   }
363 }