]> Untitled Git - lemmy.git/blob - crates/db_views_actor/src/person_mention_view.rs
Merge pull request #1850 from LemmyNet/refactor-apub
[lemmy.git] / crates / db_views_actor / src / person_mention_view.rs
1 use diesel::{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         ),
101       )
102       .left_join(
103         community_follower::table.on(
104           post::community_id
105             .eq(community_follower::community_id)
106             .and(community_follower::person_id.eq(person_id_join)),
107         ),
108       )
109       .left_join(
110         comment_saved::table.on(
111           comment::id
112             .eq(comment_saved::comment_id)
113             .and(comment_saved::person_id.eq(person_id_join)),
114         ),
115       )
116       .left_join(
117         person_block::table.on(
118           comment::creator_id
119             .eq(person_block::target_id)
120             .and(person_block::person_id.eq(person_id_join)),
121         ),
122       )
123       .left_join(
124         comment_like::table.on(
125           comment::id
126             .eq(comment_like::comment_id)
127             .and(comment_like::person_id.eq(person_id_join)),
128         ),
129       )
130       .select((
131         person_mention::all_columns,
132         comment::all_columns,
133         Person::safe_columns_tuple(),
134         post::all_columns,
135         Community::safe_columns_tuple(),
136         PersonAlias1::safe_columns_tuple(),
137         comment_aggregates::all_columns,
138         community_person_ban::all_columns.nullable(),
139         community_follower::all_columns.nullable(),
140         comment_saved::all_columns.nullable(),
141         person_block::all_columns.nullable(),
142         comment_like::score.nullable(),
143       ))
144       .first::<PersonMentionViewTuple>(conn)?;
145
146     Ok(PersonMentionView {
147       person_mention,
148       comment,
149       creator,
150       post,
151       community,
152       recipient,
153       counts,
154       creator_banned_from_community: creator_banned_from_community.is_some(),
155       subscribed: subscribed.is_some(),
156       saved: saved.is_some(),
157       creator_blocked: creator_blocked.is_some(),
158       my_vote,
159     })
160   }
161
162   /// Gets the number of unread mentions
163   pub fn get_unread_mentions(conn: &PgConnection, my_person_id: PersonId) -> Result<i64, Error> {
164     use diesel::dsl::*;
165
166     person_mention::table
167       .filter(person_mention::recipient_id.eq(my_person_id))
168       .filter(person_mention::read.eq(false))
169       .select(count(person_mention::id))
170       .first::<i64>(conn)
171   }
172 }
173
174 pub struct PersonMentionQueryBuilder<'a> {
175   conn: &'a PgConnection,
176   my_person_id: Option<PersonId>,
177   recipient_id: Option<PersonId>,
178   sort: Option<SortType>,
179   unread_only: Option<bool>,
180   page: Option<i64>,
181   limit: Option<i64>,
182 }
183
184 impl<'a> PersonMentionQueryBuilder<'a> {
185   pub fn create(conn: &'a PgConnection) -> Self {
186     PersonMentionQueryBuilder {
187       conn,
188       my_person_id: None,
189       recipient_id: None,
190       sort: None,
191       unread_only: None,
192       page: None,
193       limit: None,
194     }
195   }
196
197   pub fn sort<T: MaybeOptional<SortType>>(mut self, sort: T) -> Self {
198     self.sort = sort.get_optional();
199     self
200   }
201
202   pub fn unread_only<T: MaybeOptional<bool>>(mut self, unread_only: T) -> Self {
203     self.unread_only = unread_only.get_optional();
204     self
205   }
206
207   pub fn recipient_id<T: MaybeOptional<PersonId>>(mut self, recipient_id: T) -> Self {
208     self.recipient_id = recipient_id.get_optional();
209     self
210   }
211
212   pub fn my_person_id<T: MaybeOptional<PersonId>>(mut self, my_person_id: T) -> Self {
213     self.my_person_id = my_person_id.get_optional();
214     self
215   }
216
217   pub fn page<T: MaybeOptional<i64>>(mut self, page: T) -> Self {
218     self.page = page.get_optional();
219     self
220   }
221
222   pub fn limit<T: MaybeOptional<i64>>(mut self, limit: T) -> Self {
223     self.limit = limit.get_optional();
224     self
225   }
226
227   pub fn list(self) -> Result<Vec<PersonMentionView>, Error> {
228     use diesel::dsl::*;
229
230     // The left join below will return None in this case
231     let person_id_join = self.my_person_id.unwrap_or(PersonId(-1));
232
233     let mut query = person_mention::table
234       .inner_join(comment::table)
235       .inner_join(person::table.on(comment::creator_id.eq(person::id)))
236       .inner_join(post::table.on(comment::post_id.eq(post::id)))
237       .inner_join(community::table.on(post::community_id.eq(community::id)))
238       .inner_join(person_alias_1::table)
239       .inner_join(comment_aggregates::table.on(comment::id.eq(comment_aggregates::comment_id)))
240       .left_join(
241         community_person_ban::table.on(
242           community::id
243             .eq(community_person_ban::community_id)
244             .and(community_person_ban::person_id.eq(comment::creator_id)),
245         ),
246       )
247       .left_join(
248         community_follower::table.on(
249           post::community_id
250             .eq(community_follower::community_id)
251             .and(community_follower::person_id.eq(person_id_join)),
252         ),
253       )
254       .left_join(
255         comment_saved::table.on(
256           comment::id
257             .eq(comment_saved::comment_id)
258             .and(comment_saved::person_id.eq(person_id_join)),
259         ),
260       )
261       .left_join(
262         person_block::table.on(
263           comment::creator_id
264             .eq(person_block::target_id)
265             .and(person_block::person_id.eq(person_id_join)),
266         ),
267       )
268       .left_join(
269         comment_like::table.on(
270           comment::id
271             .eq(comment_like::comment_id)
272             .and(comment_like::person_id.eq(person_id_join)),
273         ),
274       )
275       .select((
276         person_mention::all_columns,
277         comment::all_columns,
278         Person::safe_columns_tuple(),
279         post::all_columns,
280         Community::safe_columns_tuple(),
281         PersonAlias1::safe_columns_tuple(),
282         comment_aggregates::all_columns,
283         community_person_ban::all_columns.nullable(),
284         community_follower::all_columns.nullable(),
285         comment_saved::all_columns.nullable(),
286         person_block::all_columns.nullable(),
287         comment_like::score.nullable(),
288       ))
289       .into_boxed();
290
291     if let Some(recipient_id) = self.recipient_id {
292       query = query.filter(person_mention::recipient_id.eq(recipient_id));
293     }
294
295     if self.unread_only.unwrap_or(false) {
296       query = query.filter(person_mention::read.eq(false));
297     }
298
299     query = match self.sort.unwrap_or(SortType::Hot) {
300       SortType::Hot | SortType::Active => query
301         .order_by(hot_rank(comment_aggregates::score, comment_aggregates::published).desc())
302         .then_order_by(comment_aggregates::published.desc()),
303       SortType::New | SortType::MostComments | SortType::NewComments => {
304         query.order_by(comment::published.desc())
305       }
306       SortType::TopAll => query.order_by(comment_aggregates::score.desc()),
307       SortType::TopYear => query
308         .filter(comment::published.gt(now - 1.years()))
309         .order_by(comment_aggregates::score.desc()),
310       SortType::TopMonth => query
311         .filter(comment::published.gt(now - 1.months()))
312         .order_by(comment_aggregates::score.desc()),
313       SortType::TopWeek => query
314         .filter(comment::published.gt(now - 1.weeks()))
315         .order_by(comment_aggregates::score.desc()),
316       SortType::TopDay => query
317         .filter(comment::published.gt(now - 1.days()))
318         .order_by(comment_aggregates::score.desc()),
319     };
320
321     let (limit, offset) = limit_and_offset(self.page, self.limit);
322
323     let res = query
324       .limit(limit)
325       .offset(offset)
326       .load::<PersonMentionViewTuple>(self.conn)?;
327
328     Ok(PersonMentionView::from_tuple_to_vec(res))
329   }
330 }
331
332 impl ViewToVec for PersonMentionView {
333   type DbTuple = PersonMentionViewTuple;
334   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
335     items
336       .iter()
337       .map(|a| Self {
338         person_mention: a.0.to_owned(),
339         comment: a.1.to_owned(),
340         creator: a.2.to_owned(),
341         post: a.3.to_owned(),
342         community: a.4.to_owned(),
343         recipient: a.5.to_owned(),
344         counts: a.6.to_owned(),
345         creator_banned_from_community: a.7.is_some(),
346         subscribed: a.8.is_some(),
347         saved: a.9.is_some(),
348         creator_blocked: a.10.is_some(),
349         my_vote: a.11,
350       })
351       .collect::<Vec<Self>>()
352   }
353 }