]> Untitled Git - lemmy.git/blob - crates/db_views_actor/src/comment_reply_view.rs
73972deef5314feb827b9555577d97b641c94daf
[lemmy.git] / crates / db_views_actor / src / comment_reply_view.rs
1 use crate::structs::CommentReplyView;
2 use diesel::{
3   dsl::*,
4   result::Error,
5   BoolExpressionMethods,
6   ExpressionMethods,
7   JoinOnDsl,
8   NullableExpressionMethods,
9   QueryDsl,
10 };
11 use diesel_async::RunQueryDsl;
12 use lemmy_db_schema::{
13   aggregates::structs::CommentAggregates,
14   newtypes::{CommentReplyId, PersonId},
15   schema::{
16     comment,
17     comment_aggregates,
18     comment_like,
19     comment_reply,
20     comment_saved,
21     community,
22     community_follower,
23     community_person_ban,
24     person,
25     person_block,
26     post,
27   },
28   source::{
29     comment::{Comment, CommentSaved},
30     comment_reply::CommentReply,
31     community::{Community, CommunityFollower, CommunityPersonBan, CommunitySafe},
32     person::{Person, PersonSafe},
33     person_block::PersonBlock,
34     post::Post,
35   },
36   traits::{ToSafe, ViewToVec},
37   utils::{functions::hot_rank, get_conn, limit_and_offset, DbPool},
38   CommentSortType,
39 };
40 use typed_builder::TypedBuilder;
41
42 type CommentReplyViewTuple = (
43   CommentReply,
44   Comment,
45   PersonSafe,
46   Post,
47   CommunitySafe,
48   PersonSafe,
49   CommentAggregates,
50   Option<CommunityPersonBan>,
51   Option<CommunityFollower>,
52   Option<CommentSaved>,
53   Option<PersonBlock>,
54   Option<i16>,
55 );
56
57 impl CommentReplyView {
58   pub async fn read(
59     pool: &DbPool,
60     comment_reply_id: CommentReplyId,
61     my_person_id: Option<PersonId>,
62   ) -> Result<Self, Error> {
63     let conn = &mut get_conn(pool).await?;
64     let person_alias_1 = diesel::alias!(person as person1);
65
66     // The left join below will return None in this case
67     let person_id_join = my_person_id.unwrap_or(PersonId(-1));
68
69     let (
70       comment_reply,
71       comment,
72       creator,
73       post,
74       community,
75       recipient,
76       counts,
77       creator_banned_from_community,
78       follower,
79       saved,
80       creator_blocked,
81       my_vote,
82     ) = comment_reply::table
83       .find(comment_reply_id)
84       .inner_join(comment::table)
85       .inner_join(person::table.on(comment::creator_id.eq(person::id)))
86       .inner_join(post::table.on(comment::post_id.eq(post::id)))
87       .inner_join(community::table.on(post::community_id.eq(community::id)))
88       .inner_join(person_alias_1)
89       .inner_join(comment_aggregates::table.on(comment::id.eq(comment_aggregates::comment_id)))
90       .left_join(
91         community_person_ban::table.on(
92           community::id
93             .eq(community_person_ban::community_id)
94             .and(community_person_ban::person_id.eq(comment::creator_id))
95             .and(
96               community_person_ban::expires
97                 .is_null()
98                 .or(community_person_ban::expires.gt(now)),
99             ),
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         comment_reply::all_columns,
132         comment::all_columns,
133         Person::safe_columns_tuple(),
134         post::all_columns,
135         Community::safe_columns_tuple(),
136         person_alias_1.fields(Person::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::<CommentReplyViewTuple>(conn)
145       .await?;
146
147     Ok(CommentReplyView {
148       comment_reply,
149       comment,
150       creator,
151       post,
152       community,
153       recipient,
154       counts,
155       creator_banned_from_community: creator_banned_from_community.is_some(),
156       subscribed: CommunityFollower::to_subscribed_type(&follower),
157       saved: saved.is_some(),
158       creator_blocked: creator_blocked.is_some(),
159       my_vote,
160     })
161   }
162
163   /// Gets the number of unread replies
164   pub async fn get_unread_replies(pool: &DbPool, my_person_id: PersonId) -> Result<i64, Error> {
165     use diesel::dsl::*;
166
167     let conn = &mut get_conn(pool).await?;
168
169     comment_reply::table
170       .inner_join(comment::table)
171       .filter(comment_reply::recipient_id.eq(my_person_id))
172       .filter(comment_reply::read.eq(false))
173       .filter(comment::deleted.eq(false))
174       .filter(comment::removed.eq(false))
175       .select(count(comment_reply::id))
176       .first::<i64>(conn)
177       .await
178   }
179 }
180
181 #[derive(TypedBuilder)]
182 #[builder(field_defaults(default))]
183 pub struct CommentReplyQuery<'a> {
184   #[builder(!default)]
185   pool: &'a DbPool,
186   my_person_id: Option<PersonId>,
187   recipient_id: Option<PersonId>,
188   sort: Option<CommentSortType>,
189   unread_only: Option<bool>,
190   show_bot_accounts: Option<bool>,
191   page: Option<i64>,
192   limit: Option<i64>,
193 }
194
195 impl<'a> CommentReplyQuery<'a> {
196   pub async fn list(self) -> Result<Vec<CommentReplyView>, Error> {
197     use diesel::dsl::*;
198     let conn = &mut get_conn(self.pool).await?;
199
200     let person_alias_1 = diesel::alias!(person as person1);
201
202     // The left join below will return None in this case
203     let person_id_join = self.my_person_id.unwrap_or(PersonId(-1));
204
205     let mut query = comment_reply::table
206       .inner_join(comment::table)
207       .inner_join(person::table.on(comment::creator_id.eq(person::id)))
208       .inner_join(post::table.on(comment::post_id.eq(post::id)))
209       .inner_join(community::table.on(post::community_id.eq(community::id)))
210       .inner_join(person_alias_1)
211       .inner_join(comment_aggregates::table.on(comment::id.eq(comment_aggregates::comment_id)))
212       .left_join(
213         community_person_ban::table.on(
214           community::id
215             .eq(community_person_ban::community_id)
216             .and(community_person_ban::person_id.eq(comment::creator_id))
217             .and(
218               community_person_ban::expires
219                 .is_null()
220                 .or(community_person_ban::expires.gt(now)),
221             ),
222         ),
223       )
224       .left_join(
225         community_follower::table.on(
226           post::community_id
227             .eq(community_follower::community_id)
228             .and(community_follower::person_id.eq(person_id_join)),
229         ),
230       )
231       .left_join(
232         comment_saved::table.on(
233           comment::id
234             .eq(comment_saved::comment_id)
235             .and(comment_saved::person_id.eq(person_id_join)),
236         ),
237       )
238       .left_join(
239         person_block::table.on(
240           comment::creator_id
241             .eq(person_block::target_id)
242             .and(person_block::person_id.eq(person_id_join)),
243         ),
244       )
245       .left_join(
246         comment_like::table.on(
247           comment::id
248             .eq(comment_like::comment_id)
249             .and(comment_like::person_id.eq(person_id_join)),
250         ),
251       )
252       .select((
253         comment_reply::all_columns,
254         comment::all_columns,
255         Person::safe_columns_tuple(),
256         post::all_columns,
257         Community::safe_columns_tuple(),
258         person_alias_1.fields(Person::safe_columns_tuple()),
259         comment_aggregates::all_columns,
260         community_person_ban::all_columns.nullable(),
261         community_follower::all_columns.nullable(),
262         comment_saved::all_columns.nullable(),
263         person_block::all_columns.nullable(),
264         comment_like::score.nullable(),
265       ))
266       .into_boxed();
267
268     if let Some(recipient_id) = self.recipient_id {
269       query = query.filter(comment_reply::recipient_id.eq(recipient_id));
270     }
271
272     if self.unread_only.unwrap_or(false) {
273       query = query.filter(comment_reply::read.eq(false));
274     }
275
276     if !self.show_bot_accounts.unwrap_or(true) {
277       query = query.filter(person::bot_account.eq(false));
278     };
279
280     query = match self.sort.unwrap_or(CommentSortType::Hot) {
281       CommentSortType::Hot => query
282         .then_order_by(hot_rank(comment_aggregates::score, comment_aggregates::published).desc())
283         .then_order_by(comment_aggregates::published.desc()),
284       CommentSortType::New => query.then_order_by(comment::published.desc()),
285       CommentSortType::Old => query.then_order_by(comment::published.asc()),
286       CommentSortType::Top => query.order_by(comment_aggregates::score.desc()),
287     };
288
289     let (limit, offset) = limit_and_offset(self.page, self.limit)?;
290
291     let res = query
292       .limit(limit)
293       .offset(offset)
294       .load::<CommentReplyViewTuple>(conn)
295       .await?;
296
297     Ok(CommentReplyView::from_tuple_to_vec(res))
298   }
299 }
300
301 impl ViewToVec for CommentReplyView {
302   type DbTuple = CommentReplyViewTuple;
303   fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
304     items
305       .into_iter()
306       .map(|a| Self {
307         comment_reply: a.0,
308         comment: a.1,
309         creator: a.2,
310         post: a.3,
311         community: a.4,
312         recipient: a.5,
313         counts: a.6,
314         creator_banned_from_community: a.7.is_some(),
315         subscribed: CommunityFollower::to_subscribed_type(&a.8),
316         saved: a.9.is_some(),
317         creator_blocked: a.10.is_some(),
318         my_vote: a.11,
319       })
320       .collect::<Vec<Self>>()
321   }
322 }