]> Untitled Git - lemmy.git/blobdiff - crates/db_views/src/post_view.rs
implement language tags for site/community in db and api (#2434)
[lemmy.git] / crates / db_views / src / post_view.rs
index 6d0bcb5daead62c87900f293745f9111967f08d3..9db1d095c9ef935d5bdb0740bed7be193c0488fb 100644 (file)
@@ -8,10 +8,10 @@ use lemmy_db_schema::{
     community_block,
     community_follower,
     community_person_ban,
-    language,
     local_user_language,
     person,
     person_block,
+    person_post_aggregates,
     post,
     post_aggregates,
     post_like,
@@ -20,7 +20,6 @@ use lemmy_db_schema::{
   },
   source::{
     community::{Community, CommunityFollower, CommunityPersonBan, CommunitySafe},
-    language::Language,
     local_user::LocalUser,
     person::{Person, PersonSafe},
     person_block::PersonBlock,
@@ -45,12 +44,14 @@ type PostViewTuple = (
   Option<PostRead>,
   Option<PersonBlock>,
   Option<i16>,
-  Language,
+  i64,
 );
 
+sql_function!(fn coalesce(x: sql_types::Nullable<sql_types::BigInt>, y: sql_types::BigInt) -> sql_types::BigInt);
+
 impl PostView {
   pub fn read(
-    conn: &PgConnection,
+    conn: &mut PgConnection,
     post_id: PostId,
     my_person_id: Option<PersonId>,
   ) -> Result<Self, Error> {
@@ -67,7 +68,7 @@ impl PostView {
       read,
       creator_blocked,
       post_like,
-      language,
+      unread_comments,
     ) = post::table
       .find(post_id)
       .inner_join(person::table)
@@ -120,7 +121,13 @@ impl PostView {
             .and(post_like::person_id.eq(person_id_join)),
         ),
       )
-      .inner_join(language::table)
+      .left_join(
+        person_post_aggregates::table.on(
+          post::id
+            .eq(person_post_aggregates::post_id)
+            .and(person_post_aggregates::person_id.eq(person_id_join)),
+        ),
+      )
       .select((
         post::all_columns,
         Person::safe_columns_tuple(),
@@ -132,7 +139,10 @@ impl PostView {
         post_read::all_columns.nullable(),
         person_block::all_columns.nullable(),
         post_like::score.nullable(),
-        language::all_columns,
+        coalesce(
+          post_aggregates::comments.nullable() - person_post_aggregates::read_comments.nullable(),
+          post_aggregates::comments,
+        ),
       ))
       .first::<PostViewTuple>(conn)?;
 
@@ -155,7 +165,7 @@ impl PostView {
       read: read.is_some(),
       creator_blocked: creator_blocked.is_some(),
       my_vote,
-      language,
+      unread_comments,
     })
   }
 }
@@ -164,7 +174,7 @@ impl PostView {
 #[builder(field_defaults(default))]
 pub struct PostQuery<'a> {
   #[builder(!default)]
-  conn: &'a PgConnection,
+  conn: &'a mut PgConnection,
   listing_type: Option<ListingType>,
   sort: Option<SortType>,
   creator_id: Option<PersonId>,
@@ -244,7 +254,13 @@ impl<'a> PostQuery<'a> {
             .and(post_like::person_id.eq(person_id_join)),
         ),
       )
-      .inner_join(language::table)
+      .left_join(
+        person_post_aggregates::table.on(
+          post::id
+            .eq(person_post_aggregates::post_id)
+            .and(person_post_aggregates::person_id.eq(person_id_join)),
+        ),
+      )
       .left_join(
         local_user_language::table.on(
           post::language_id
@@ -263,7 +279,10 @@ impl<'a> PostQuery<'a> {
         post_read::all_columns.nullable(),
         person_block::all_columns.nullable(),
         post_like::score.nullable(),
-        language::all_columns,
+        coalesce(
+          post_aggregates::comments.nullable() - person_post_aggregates::read_comments.nullable(),
+          post_aggregates::comments,
+        ),
       ))
       .into_boxed();
 
@@ -421,7 +440,7 @@ impl ViewToVec for PostView {
         read: a.7.is_some(),
         creator_blocked: a.8.is_some(),
         my_vote: a.9,
-        language: a.10,
+        unread_comments: a.10,
       })
       .collect::<Vec<Self>>()
   }
@@ -435,11 +454,11 @@ mod tests {
     aggregates::structs::PostAggregates,
     newtypes::LanguageId,
     source::{
+      actor_language::LocalUserLanguage,
       community::*,
       community_block::{CommunityBlock, CommunityBlockForm},
       language::Language,
       local_user::{LocalUser, LocalUserForm},
-      local_user_language::LocalUserLanguage,
       person::*,
       person_block::{PersonBlock, PersonBlockForm},
       post::*,
@@ -460,7 +479,7 @@ mod tests {
     inserted_post: Post,
   }
 
-  fn init_data(conn: &PgConnection) -> Data {
+  fn init_data(conn: &mut PgConnection) -> Data {
     let person_name = "tegan".to_string();
 
     let new_person = PersonForm {
@@ -477,8 +496,6 @@ mod tests {
       ..Default::default()
     };
     let inserted_local_user = LocalUser::create(conn, &local_user_form).unwrap();
-    // update user languages to all
-    LocalUserLanguage::update_user_languages(conn, None, inserted_local_user.id).unwrap();
 
     let new_bot = PersonForm {
       name: "mybot".to_string(),
@@ -555,124 +572,21 @@ mod tests {
     }
   }
 
-  fn cleanup(data: Data, conn: &PgConnection) {
-    let num_deleted = Post::delete(conn, data.inserted_post.id).unwrap();
-    Community::delete(conn, data.inserted_community.id).unwrap();
-    Person::delete(conn, data.inserted_person.id).unwrap();
-    Person::delete(conn, data.inserted_bot.id).unwrap();
-    Person::delete(conn, data.inserted_blocked_person.id).unwrap();
-    assert_eq!(1, num_deleted);
-  }
-
-  fn expected_post_listing(data: &Data, conn: &PgConnection) -> PostView {
-    let (inserted_person, inserted_community, inserted_post) = (
-      &data.inserted_person,
-      &data.inserted_community,
-      &data.inserted_post,
-    );
-    let agg = PostAggregates::read(conn, inserted_post.id).unwrap();
-
-    PostView {
-      post: Post {
-        id: inserted_post.id,
-        name: inserted_post.name.clone(),
-        creator_id: inserted_person.id,
-        url: None,
-        body: None,
-        published: inserted_post.published,
-        updated: None,
-        community_id: inserted_community.id,
-        removed: false,
-        deleted: false,
-        locked: false,
-        stickied: false,
-        nsfw: false,
-        embed_title: None,
-        embed_description: None,
-        embed_video_url: None,
-        thumbnail_url: None,
-        ap_id: inserted_post.ap_id.to_owned(),
-        local: true,
-        language_id: LanguageId(47),
-      },
-      my_vote: None,
-      creator: PersonSafe {
-        id: inserted_person.id,
-        name: inserted_person.name.clone(),
-        display_name: None,
-        published: inserted_person.published,
-        avatar: None,
-        actor_id: inserted_person.actor_id.to_owned(),
-        local: true,
-        admin: false,
-        bot_account: false,
-        banned: false,
-        deleted: false,
-        bio: None,
-        banner: None,
-        updated: None,
-        inbox_url: inserted_person.inbox_url.to_owned(),
-        shared_inbox_url: None,
-        matrix_user_id: None,
-        ban_expires: None,
-      },
-      creator_banned_from_community: false,
-      community: CommunitySafe {
-        id: inserted_community.id,
-        name: inserted_community.name.clone(),
-        icon: None,
-        removed: false,
-        deleted: false,
-        nsfw: false,
-        actor_id: inserted_community.actor_id.to_owned(),
-        local: true,
-        title: "nada".to_owned(),
-        description: None,
-        updated: None,
-        banner: None,
-        hidden: false,
-        posting_restricted_to_mods: false,
-        published: inserted_community.published,
-      },
-      counts: PostAggregates {
-        id: agg.id,
-        post_id: inserted_post.id,
-        comments: 0,
-        score: 0,
-        upvotes: 0,
-        downvotes: 0,
-        stickied: false,
-        published: agg.published,
-        newest_comment_time_necro: inserted_post.published,
-        newest_comment_time: inserted_post.published,
-      },
-      subscribed: SubscribedType::NotSubscribed,
-      read: false,
-      saved: false,
-      creator_blocked: false,
-      language: Language {
-        id: LanguageId(47),
-        code: "fr".to_string(),
-        name: "Français".to_string(),
-      },
-    }
-  }
-
   #[test]
   #[serial]
   fn post_listing_with_person() {
-    let conn = establish_unpooled_connection();
-    let data = init_data(&conn);
+    let conn = &mut establish_unpooled_connection();
+    let data = init_data(conn);
 
     let local_user_form = LocalUserForm {
       show_bot_accounts: Some(false),
       ..Default::default()
     };
     let inserted_local_user =
-      LocalUser::update(&conn, data.inserted_local_user.id, &local_user_form).unwrap();
+      LocalUser::update(conn, data.inserted_local_user.id, &local_user_form).unwrap();
 
     let read_post_listing = PostQuery::builder()
-      .conn(&conn)
+      .conn(conn)
       .sort(Some(SortType::New))
       .community_id(Some(data.inserted_community.id))
       .local_user(Some(&inserted_local_user))
@@ -681,9 +595,9 @@ mod tests {
       .unwrap();
 
     let post_listing_single_with_person =
-      PostView::read(&conn, data.inserted_post.id, Some(data.inserted_person.id)).unwrap();
+      PostView::read(conn, data.inserted_post.id, Some(data.inserted_person.id)).unwrap();
 
-    let mut expected_post_listing_with_user = expected_post_listing(&data, &conn);
+    let mut expected_post_listing_with_user = expected_post_view(&data, conn);
 
     // Should be only one person, IE the bot post, and blocked should be missing
     assert_eq!(1, read_post_listing.len());
@@ -700,10 +614,10 @@ mod tests {
       ..Default::default()
     };
     let inserted_local_user =
-      LocalUser::update(&conn, data.inserted_local_user.id, &local_user_form).unwrap();
+      LocalUser::update(conn, data.inserted_local_user.id, &local_user_form).unwrap();
 
     let post_listings_with_bots = PostQuery::builder()
-      .conn(&conn)
+      .conn(conn)
       .sort(Some(SortType::New))
       .community_id(Some(data.inserted_community.id))
       .local_user(Some(&inserted_local_user))
@@ -713,17 +627,17 @@ mod tests {
     // should include bot post which has "undetermined" language
     assert_eq!(2, post_listings_with_bots.len());
 
-    cleanup(data, &conn);
+    cleanup(data, conn);
   }
 
   #[test]
   #[serial]
   fn post_listing_no_person() {
-    let conn = establish_unpooled_connection();
-    let data = init_data(&conn);
+    let conn = &mut establish_unpooled_connection();
+    let data = init_data(conn);
 
     let read_post_listing_multiple_no_person = PostQuery::builder()
-      .conn(&conn)
+      .conn(conn)
       .sort(Some(SortType::New))
       .community_id(Some(data.inserted_community.id))
       .build()
@@ -731,9 +645,9 @@ mod tests {
       .unwrap();
 
     let read_post_listing_single_no_person =
-      PostView::read(&conn, data.inserted_post.id, None).unwrap();
+      PostView::read(conn, data.inserted_post.id, None).unwrap();
 
-    let expected_post_listing_no_person = expected_post_listing(&data, &conn);
+    let expected_post_listing_no_person = expected_post_view(&data, conn);
 
     // Should be 2 posts, with the bot post, and the blocked
     assert_eq!(3, read_post_listing_multiple_no_person.len());
@@ -747,23 +661,23 @@ mod tests {
       read_post_listing_single_no_person
     );
 
-    cleanup(data, &conn);
+    cleanup(data, conn);
   }
 
   #[test]
   #[serial]
   fn post_listing_block_community() {
-    let conn = establish_unpooled_connection();
-    let data = init_data(&conn);
+    let conn = &mut establish_unpooled_connection();
+    let data = init_data(conn);
 
     let community_block = CommunityBlockForm {
       person_id: data.inserted_person.id,
       community_id: data.inserted_community.id,
     };
-    CommunityBlock::block(&conn, &community_block).unwrap();
+    CommunityBlock::block(conn, &community_block).unwrap();
 
     let read_post_listings_with_person_after_block = PostQuery::builder()
-      .conn(&conn)
+      .conn(conn)
       .sort(Some(SortType::New))
       .community_id(Some(data.inserted_community.id))
       .local_user(Some(&data.inserted_local_user))
@@ -773,15 +687,15 @@ mod tests {
     // Should be 0 posts after the community block
     assert_eq!(0, read_post_listings_with_person_after_block.len());
 
-    CommunityBlock::unblock(&conn, &community_block).unwrap();
-    cleanup(data, &conn);
+    CommunityBlock::unblock(conn, &community_block).unwrap();
+    cleanup(data, conn);
   }
 
   #[test]
   #[serial]
   fn post_listing_like() {
-    let conn = establish_unpooled_connection();
-    let data = init_data(&conn);
+    let conn = &mut establish_unpooled_connection();
+    let data = init_data(conn);
 
     let post_like_form = PostLikeForm {
       post_id: data.inserted_post.id,
@@ -789,7 +703,7 @@ mod tests {
       score: 1,
     };
 
-    let inserted_post_like = PostLike::like(&conn, &post_like_form).unwrap();
+    let inserted_post_like = PostLike::like(conn, &post_like_form).unwrap();
 
     let expected_post_like = PostLike {
       id: inserted_post_like.id,
@@ -801,18 +715,18 @@ mod tests {
     assert_eq!(expected_post_like, inserted_post_like);
 
     let like_removed =
-      PostLike::remove(&conn, data.inserted_person.id, data.inserted_post.id).unwrap();
+      PostLike::remove(conn, data.inserted_person.id, data.inserted_post.id).unwrap();
     assert_eq!(1, like_removed);
-    cleanup(data, &conn);
+    cleanup(data, conn);
   }
 
   #[test]
   #[serial]
   fn post_listing_person_language() {
-    let conn = establish_unpooled_connection();
-    let data = init_data(&conn);
+    let conn = &mut establish_unpooled_connection();
+    let data = init_data(conn);
 
-    let spanish_id = Language::read_id_from_code(&conn, "es").unwrap();
+    let spanish_id = Language::read_id_from_code(conn, "es").unwrap();
     let post_spanish = PostForm {
       name: "asffgdsc".to_string(),
       creator_id: data.inserted_person.id,
@@ -821,10 +735,10 @@ mod tests {
       ..PostForm::default()
     };
 
-    Post::create(&conn, &post_spanish).unwrap();
+    Post::create(conn, &post_spanish).unwrap();
 
     let post_listings_all = PostQuery::builder()
-      .conn(&conn)
+      .conn(conn)
       .sort(Some(SortType::New))
       .local_user(Some(&data.inserted_local_user))
       .build()
@@ -834,16 +748,11 @@ mod tests {
     // no language filters specified, all posts should be returned
     assert_eq!(3, post_listings_all.len());
 
-    let french_id = Language::read_id_from_code(&conn, "fr").unwrap();
-    LocalUserLanguage::update_user_languages(
-      &conn,
-      Some(vec![french_id]),
-      data.inserted_local_user.id,
-    )
-    .unwrap();
+    let french_id = Language::read_id_from_code(conn, "fr").unwrap();
+    LocalUserLanguage::update(conn, vec![french_id], data.inserted_local_user.id).unwrap();
 
     let post_listing_french = PostQuery::builder()
-      .conn(&conn)
+      .conn(conn)
       .sort(Some(SortType::New))
       .local_user(Some(&data.inserted_local_user))
       .build()
@@ -854,15 +763,15 @@ mod tests {
     assert_eq!(1, post_listing_french.len());
     assert_eq!(french_id, post_listing_french[0].post.language_id);
 
-    let undetermined_id = Language::read_id_from_code(&conn, "und").unwrap();
-    LocalUserLanguage::update_user_languages(
-      &conn,
-      Some(vec![french_id, undetermined_id]),
+    let undetermined_id = Language::read_id_from_code(conn, "und").unwrap();
+    LocalUserLanguage::update(
+      conn,
+      vec![french_id, undetermined_id],
       data.inserted_local_user.id,
     )
     .unwrap();
     let post_listings_french_und = PostQuery::builder()
-      .conn(&conn)
+      .conn(conn)
       .sort(Some(SortType::New))
       .local_user(Some(&data.inserted_local_user))
       .build()
@@ -877,6 +786,105 @@ mod tests {
     );
     assert_eq!(french_id, post_listings_french_und[1].post.language_id);
 
-    cleanup(data, &conn);
+    cleanup(data, conn);
+  }
+
+  fn cleanup(data: Data, conn: &mut PgConnection) {
+    let num_deleted = Post::delete(conn, data.inserted_post.id).unwrap();
+    Community::delete(conn, data.inserted_community.id).unwrap();
+    Person::delete(conn, data.inserted_person.id).unwrap();
+    Person::delete(conn, data.inserted_bot.id).unwrap();
+    Person::delete(conn, data.inserted_blocked_person.id).unwrap();
+    assert_eq!(1, num_deleted);
+  }
+
+  fn expected_post_view(data: &Data, conn: &mut PgConnection) -> PostView {
+    let (inserted_person, inserted_community, inserted_post) = (
+      &data.inserted_person,
+      &data.inserted_community,
+      &data.inserted_post,
+    );
+    let agg = PostAggregates::read(conn, inserted_post.id).unwrap();
+
+    PostView {
+      post: Post {
+        id: inserted_post.id,
+        name: inserted_post.name.clone(),
+        creator_id: inserted_person.id,
+        url: None,
+        body: None,
+        published: inserted_post.published,
+        updated: None,
+        community_id: inserted_community.id,
+        removed: false,
+        deleted: false,
+        locked: false,
+        stickied: false,
+        nsfw: false,
+        embed_title: None,
+        embed_description: None,
+        embed_video_url: None,
+        thumbnail_url: None,
+        ap_id: inserted_post.ap_id.to_owned(),
+        local: true,
+        language_id: LanguageId(47),
+      },
+      my_vote: None,
+      unread_comments: 0,
+      creator: PersonSafe {
+        id: inserted_person.id,
+        name: inserted_person.name.clone(),
+        display_name: None,
+        published: inserted_person.published,
+        avatar: None,
+        actor_id: inserted_person.actor_id.to_owned(),
+        local: true,
+        admin: false,
+        bot_account: false,
+        banned: false,
+        deleted: false,
+        bio: None,
+        banner: None,
+        updated: None,
+        inbox_url: inserted_person.inbox_url.to_owned(),
+        shared_inbox_url: None,
+        matrix_user_id: None,
+        ban_expires: None,
+      },
+      creator_banned_from_community: false,
+      community: CommunitySafe {
+        id: inserted_community.id,
+        name: inserted_community.name.clone(),
+        icon: None,
+        removed: false,
+        deleted: false,
+        nsfw: false,
+        actor_id: inserted_community.actor_id.to_owned(),
+        local: true,
+        title: "nada".to_owned(),
+        description: None,
+        updated: None,
+        banner: None,
+        hidden: false,
+        posting_restricted_to_mods: false,
+        published: inserted_community.published,
+      },
+      counts: PostAggregates {
+        id: agg.id,
+        post_id: inserted_post.id,
+        comments: 0,
+        score: 0,
+        upvotes: 0,
+        downvotes: 0,
+        stickied: false,
+        published: agg.published,
+        newest_comment_time_necro: inserted_post.published,
+        newest_comment_time: inserted_post.published,
+      },
+      subscribed: SubscribedType::NotSubscribed,
+      read: false,
+      saved: false,
+      creator_blocked: false,
+    }
   }
 }