4 // The faked schema since diesel doesn't do views
10 parent_id -> Nullable<Int4>,
14 published -> Timestamp,
15 updated -> Nullable<Timestamp>,
20 community_actor_id -> Text,
21 community_local -> Bool,
22 community_name -> Varchar,
24 banned_from_community -> Bool,
25 creator_actor_id -> Text,
26 creator_local -> Bool,
27 creator_name -> Varchar,
28 creator_avatar -> Nullable<Text>,
33 user_id -> Nullable<Int4>,
34 my_vote -> Nullable<Int4>,
35 subscribed -> Nullable<Bool>,
36 saved -> Nullable<Bool>,
45 parent_id -> Nullable<Int4>,
49 published -> Timestamp,
50 updated -> Nullable<Timestamp>,
55 community_actor_id -> Text,
56 community_local -> Bool,
57 community_name -> Varchar,
59 banned_from_community -> Bool,
60 creator_actor_id -> Text,
61 creator_local -> Bool,
62 creator_name -> Varchar,
63 creator_avatar -> Nullable<Text>,
68 user_id -> Nullable<Int4>,
69 my_vote -> Nullable<Int4>,
70 subscribed -> Nullable<Bool>,
71 saved -> Nullable<Bool>,
76 Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize, QueryableByName, Clone,
78 #[table_name = "comment_view"]
79 pub struct CommentView {
83 pub parent_id: Option<i32>,
87 pub published: chrono::NaiveDateTime,
88 pub updated: Option<chrono::NaiveDateTime>,
92 pub community_id: i32,
93 pub community_actor_id: String,
94 pub community_local: bool,
95 pub community_name: String,
97 pub banned_from_community: bool,
98 pub creator_actor_id: String,
99 pub creator_local: bool,
100 pub creator_name: String,
101 pub creator_avatar: Option<String>,
106 pub user_id: Option<i32>,
107 pub my_vote: Option<i32>,
108 pub subscribed: Option<bool>,
109 pub saved: Option<bool>,
112 pub struct CommentQueryBuilder<'a> {
113 conn: &'a PgConnection,
114 query: super::comment_view::comment_mview::BoxedQuery<'a, Pg>,
115 listing_type: ListingType,
117 for_community_id: Option<i32>,
118 for_post_id: Option<i32>,
119 for_creator_id: Option<i32>,
120 search_term: Option<String>,
121 my_user_id: Option<i32>,
127 impl<'a> CommentQueryBuilder<'a> {
128 pub fn create(conn: &'a PgConnection) -> Self {
129 use super::comment_view::comment_mview::dsl::*;
131 let query = comment_mview.into_boxed();
133 CommentQueryBuilder {
136 listing_type: ListingType::All,
137 sort: &SortType::New,
138 for_community_id: None,
140 for_creator_id: None,
149 pub fn listing_type(mut self, listing_type: ListingType) -> Self {
150 self.listing_type = listing_type;
154 pub fn sort(mut self, sort: &'a SortType) -> Self {
159 pub fn for_post_id<T: MaybeOptional<i32>>(mut self, for_post_id: T) -> Self {
160 self.for_post_id = for_post_id.get_optional();
164 pub fn for_creator_id<T: MaybeOptional<i32>>(mut self, for_creator_id: T) -> Self {
165 self.for_creator_id = for_creator_id.get_optional();
169 pub fn for_community_id<T: MaybeOptional<i32>>(mut self, for_community_id: T) -> Self {
170 self.for_community_id = for_community_id.get_optional();
174 pub fn search_term<T: MaybeOptional<String>>(mut self, search_term: T) -> Self {
175 self.search_term = search_term.get_optional();
179 pub fn my_user_id<T: MaybeOptional<i32>>(mut self, my_user_id: T) -> Self {
180 self.my_user_id = my_user_id.get_optional();
184 pub fn saved_only(mut self, saved_only: bool) -> Self {
185 self.saved_only = saved_only;
189 pub fn page<T: MaybeOptional<i64>>(mut self, page: T) -> Self {
190 self.page = page.get_optional();
194 pub fn limit<T: MaybeOptional<i64>>(mut self, limit: T) -> Self {
195 self.limit = limit.get_optional();
199 pub fn list(self) -> Result<Vec<CommentView>, Error> {
200 use super::comment_view::comment_mview::dsl::*;
202 let mut query = self.query;
204 // The view lets you pass a null user_id, if you're not logged in
205 if let Some(my_user_id) = self.my_user_id {
206 query = query.filter(user_id.eq(my_user_id));
208 query = query.filter(user_id.is_null());
211 if let Some(for_creator_id) = self.for_creator_id {
212 query = query.filter(creator_id.eq(for_creator_id));
215 if let Some(for_community_id) = self.for_community_id {
216 query = query.filter(community_id.eq(for_community_id));
219 if let Some(for_post_id) = self.for_post_id {
220 query = query.filter(post_id.eq(for_post_id));
223 if let Some(search_term) = self.search_term {
224 query = query.filter(content.ilike(fuzzy_search(&search_term)));
227 if let ListingType::Subscribed = self.listing_type {
228 query = query.filter(subscribed.eq(true));
232 query = query.filter(saved.eq(true));
235 query = match self.sort {
236 SortType::Hot => query
237 .order_by(hot_rank.desc())
238 .then_order_by(published.desc()),
239 SortType::New => query.order_by(published.desc()),
240 SortType::TopAll => query.order_by(score.desc()),
241 SortType::TopYear => query
242 .filter(published.gt(now - 1.years()))
243 .order_by(score.desc()),
244 SortType::TopMonth => query
245 .filter(published.gt(now - 1.months()))
246 .order_by(score.desc()),
247 SortType::TopWeek => query
248 .filter(published.gt(now - 1.weeks()))
249 .order_by(score.desc()),
250 SortType::TopDay => query
251 .filter(published.gt(now - 1.days()))
252 .order_by(score.desc()),
253 // _ => query.order_by(published.desc()),
256 let (limit, offset) = limit_and_offset(self.page, self.limit);
258 // Note: deleted and removed comments are done on the front side
262 .load::<CommentView>(self.conn)
269 from_comment_id: i32,
270 my_user_id: Option<i32>,
271 ) -> Result<Self, Error> {
272 use super::comment_view::comment_mview::dsl::*;
273 let mut query = comment_mview.into_boxed();
275 // The view lets you pass a null user_id, if you're not logged in
276 if let Some(my_user_id) = my_user_id {
277 query = query.filter(user_id.eq(my_user_id));
279 query = query.filter(user_id.is_null());
283 .filter(id.eq(from_comment_id))
284 .order_by(published.desc());
286 query.first::<Self>(conn)
290 // The faked schema since diesel doesn't do views
296 parent_id -> Nullable<Int4>,
300 published -> Timestamp,
301 updated -> Nullable<Timestamp>,
305 community_id -> Int4,
306 community_actor_id -> Text,
307 community_local -> Bool,
308 community_name -> Varchar,
310 banned_from_community -> Bool,
311 creator_actor_id -> Text,
312 creator_local -> Bool,
313 creator_name -> Varchar,
314 creator_avatar -> Nullable<Text>,
319 user_id -> Nullable<Int4>,
320 my_vote -> Nullable<Int4>,
321 subscribed -> Nullable<Bool>,
322 saved -> Nullable<Bool>,
323 recipient_id -> Int4,
328 Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize, QueryableByName, Clone,
330 #[table_name = "reply_view"]
331 pub struct ReplyView {
335 pub parent_id: Option<i32>,
339 pub published: chrono::NaiveDateTime,
340 pub updated: Option<chrono::NaiveDateTime>,
344 pub community_id: i32,
345 pub community_actor_id: String,
346 pub community_local: bool,
347 pub community_name: String,
349 pub banned_from_community: bool,
350 pub creator_actor_id: String,
351 pub creator_local: bool,
352 pub creator_name: String,
353 pub creator_avatar: Option<String>,
358 pub user_id: Option<i32>,
359 pub my_vote: Option<i32>,
360 pub subscribed: Option<bool>,
361 pub saved: Option<bool>,
362 pub recipient_id: i32,
365 pub struct ReplyQueryBuilder<'a> {
366 conn: &'a PgConnection,
367 query: super::comment_view::reply_view::BoxedQuery<'a, Pg>,
375 impl<'a> ReplyQueryBuilder<'a> {
376 pub fn create(conn: &'a PgConnection, for_user_id: i32) -> Self {
377 use super::comment_view::reply_view::dsl::*;
379 let query = reply_view.into_boxed();
385 sort: &SortType::New,
392 pub fn sort(mut self, sort: &'a SortType) -> Self {
397 pub fn unread_only(mut self, unread_only: bool) -> Self {
398 self.unread_only = unread_only;
402 pub fn page<T: MaybeOptional<i64>>(mut self, page: T) -> Self {
403 self.page = page.get_optional();
407 pub fn limit<T: MaybeOptional<i64>>(mut self, limit: T) -> Self {
408 self.limit = limit.get_optional();
412 pub fn list(self) -> Result<Vec<ReplyView>, Error> {
413 use super::comment_view::reply_view::dsl::*;
415 let mut query = self.query;
418 .filter(user_id.eq(self.for_user_id))
419 .filter(recipient_id.eq(self.for_user_id))
420 .filter(deleted.eq(false))
421 .filter(removed.eq(false));
423 if self.unread_only {
424 query = query.filter(read.eq(false));
427 query = match self.sort {
428 // SortType::Hot => query.order_by(hot_rank.desc()),
429 SortType::New => query.order_by(published.desc()),
430 SortType::TopAll => query.order_by(score.desc()),
431 SortType::TopYear => query
432 .filter(published.gt(now - 1.years()))
433 .order_by(score.desc()),
434 SortType::TopMonth => query
435 .filter(published.gt(now - 1.months()))
436 .order_by(score.desc()),
437 SortType::TopWeek => query
438 .filter(published.gt(now - 1.weeks()))
439 .order_by(score.desc()),
440 SortType::TopDay => query
441 .filter(published.gt(now - 1.days()))
442 .order_by(score.desc()),
443 _ => query.order_by(published.desc()),
446 let (limit, offset) = limit_and_offset(self.page, self.limit);
450 .load::<ReplyView>(self.conn)
456 use super::super::comment::*;
457 use super::super::community::*;
458 use super::super::post::*;
459 use super::super::user::*;
463 let conn = establish_unpooled_connection();
465 let new_user = UserForm {
466 name: "timmy".into(),
467 preferred_username: None,
468 password_encrypted: "nope".into(),
470 matrix_user_id: None,
476 theme: "darkly".into(),
477 default_sort_type: SortType::Hot as i16,
478 default_listing_type: ListingType::Subscribed as i16,
479 lang: "browser".into(),
481 send_notifications_to_email: false,
482 actor_id: "changeme".into(),
487 last_refreshed_at: None,
490 let inserted_user = User_::create(&conn, &new_user).unwrap();
492 let new_community = CommunityForm {
493 name: "test community 5".to_string(),
494 title: "nada".to_owned(),
497 creator_id: inserted_user.id,
502 actor_id: "changeme".into(),
506 last_refreshed_at: None,
510 let inserted_community = Community::create(&conn, &new_community).unwrap();
512 let new_post = PostForm {
513 name: "A test post 2".into(),
514 creator_id: inserted_user.id,
517 community_id: inserted_community.id,
525 embed_description: None,
528 ap_id: "changeme".into(),
533 let inserted_post = Post::create(&conn, &new_post).unwrap();
535 let comment_form = CommentForm {
536 content: "A test comment 32".into(),
537 creator_id: inserted_user.id,
538 post_id: inserted_post.id,
544 ap_id: "changeme".into(),
548 let inserted_comment = Comment::create(&conn, &comment_form).unwrap();
550 let comment_like_form = CommentLikeForm {
551 comment_id: inserted_comment.id,
552 post_id: inserted_post.id,
553 user_id: inserted_user.id,
557 let _inserted_comment_like = CommentLike::like(&conn, &comment_like_form).unwrap();
559 let expected_comment_view_no_user = CommentView {
560 id: inserted_comment.id,
561 content: "A test comment 32".into(),
562 creator_id: inserted_user.id,
563 post_id: inserted_post.id,
564 community_id: inserted_community.id,
565 community_name: inserted_community.name.to_owned(),
571 banned_from_community: false,
572 published: inserted_comment.published,
574 creator_name: inserted_user.name.to_owned(),
575 creator_avatar: None,
584 ap_id: "changeme".to_string(),
586 community_actor_id: inserted_community.actor_id.to_owned(),
587 community_local: true,
588 creator_actor_id: inserted_user.actor_id.to_owned(),
592 let expected_comment_view_with_user = CommentView {
593 id: inserted_comment.id,
594 content: "A test comment 32".into(),
595 creator_id: inserted_user.id,
596 post_id: inserted_post.id,
597 community_id: inserted_community.id,
598 community_name: inserted_community.name.to_owned(),
604 banned_from_community: false,
605 published: inserted_comment.published,
607 creator_name: inserted_user.name.to_owned(),
608 creator_avatar: None,
613 user_id: Some(inserted_user.id),
617 ap_id: "changeme".to_string(),
619 community_actor_id: inserted_community.actor_id.to_owned(),
620 community_local: true,
621 creator_actor_id: inserted_user.actor_id.to_owned(),
625 let mut read_comment_views_no_user = CommentQueryBuilder::create(&conn)
626 .for_post_id(inserted_post.id)
629 read_comment_views_no_user[0].hot_rank = 0;
631 let mut read_comment_views_with_user = CommentQueryBuilder::create(&conn)
632 .for_post_id(inserted_post.id)
633 .my_user_id(inserted_user.id)
636 read_comment_views_with_user[0].hot_rank = 0;
638 let like_removed = CommentLike::remove(&conn, &comment_like_form).unwrap();
639 let num_deleted = Comment::delete(&conn, inserted_comment.id).unwrap();
640 Post::delete(&conn, inserted_post.id).unwrap();
641 Community::delete(&conn, inserted_community.id).unwrap();
642 User_::delete(&conn, inserted_user.id).unwrap();
644 assert_eq!(expected_comment_view_no_user, read_comment_views_no_user[0]);
646 expected_comment_view_with_user,
647 read_comment_views_with_user[0]
649 assert_eq!(1, num_deleted);
650 assert_eq!(1, like_removed);