diff --git a/crates/api/src/post/hide.rs b/crates/api/src/post/hide.rs index f7c21ef31..58464421c 100644 --- a/crates/api/src/post/hide.rs +++ b/crates/api/src/post/hide.rs @@ -1,34 +1,39 @@ use actix_web::web::{Data, Json}; -use lemmy_api_common::{context::LemmyContext, post::HidePost, SuccessResponse}; +use lemmy_api_common::{ + context::LemmyContext, + post::{HidePost, PostResponse}, +}; use lemmy_db_schema::source::post::PostHide; -use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult, MAX_API_PARAM_ELEMENTS}; -use std::collections::HashSet; +use lemmy_db_views::structs::{LocalUserView, PostView}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn hide_post( data: Json, context: Data, local_user_view: LocalUserView, -) -> LemmyResult> { - let post_ids = HashSet::from_iter(data.post_ids.clone()); - - if post_ids.len() > MAX_API_PARAM_ELEMENTS { - Err(LemmyErrorType::TooManyItems)?; - } - +) -> LemmyResult> { let person_id = local_user_view.person.id; + let post_id = data.post_id; // Mark the post as hidden / unhidden if data.hide { - PostHide::hide(&mut context.pool(), post_ids, person_id) + PostHide::hide(&mut context.pool(), post_id, person_id) .await .with_lemmy_type(LemmyErrorType::CouldntHidePost)?; } else { - PostHide::unhide(&mut context.pool(), post_ids, person_id) + PostHide::unhide(&mut context.pool(), post_id, person_id) .await .with_lemmy_type(LemmyErrorType::CouldntHidePost)?; } - Ok(Json(SuccessResponse::default())) + let post_view = PostView::read( + &mut context.pool(), + post_id, + Some(&local_user_view.local_user), + false, + ) + .await?; + + Ok(Json(PostResponse { post_view })) } diff --git a/crates/api/src/post/mark_read.rs b/crates/api/src/post/mark_read.rs index 3e534675a..893be56b6 100644 --- a/crates/api/src/post/mark_read.rs +++ b/crates/api/src/post/mark_read.rs @@ -1,34 +1,38 @@ use actix_web::web::{Data, Json}; -use lemmy_api_common::{context::LemmyContext, post::MarkPostAsRead, SuccessResponse}; +use lemmy_api_common::{ + context::LemmyContext, + post::{MarkPostAsRead, PostResponse}, +}; use lemmy_db_schema::source::post::PostRead; -use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult, MAX_API_PARAM_ELEMENTS}; -use std::collections::HashSet; +use lemmy_db_views::structs::{LocalUserView, PostView}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] pub async fn mark_post_as_read( data: Json, context: Data, local_user_view: LocalUserView, -) -> LemmyResult> { - let post_ids = HashSet::from_iter(data.post_ids.clone()); - - if post_ids.len() > MAX_API_PARAM_ELEMENTS { - Err(LemmyErrorType::TooManyItems)?; - } - +) -> LemmyResult> { let person_id = local_user_view.person.id; + let post_id = data.post_id; // Mark the post as read / unread if data.read { - PostRead::mark_as_read(&mut context.pool(), post_ids, person_id) + PostRead::mark_as_read(&mut context.pool(), post_id, person_id) .await .with_lemmy_type(LemmyErrorType::CouldntMarkPostAsRead)?; } else { - PostRead::mark_as_unread(&mut context.pool(), post_ids, person_id) + PostRead::mark_as_unread(&mut context.pool(), post_id, person_id) .await .with_lemmy_type(LemmyErrorType::CouldntMarkPostAsRead)?; } + let post_view = PostView::read( + &mut context.pool(), + post_id, + Some(&local_user_view.local_user), + false, + ) + .await?; - Ok(Json(SuccessResponse::default())) + Ok(Json(PostResponse { post_view })) } diff --git a/crates/api_common/src/post.rs b/crates/api_common/src/post.rs index 44436fa84..cc278d8e0 100644 --- a/crates/api_common/src/post.rs +++ b/crates/api_common/src/post.rs @@ -152,7 +152,7 @@ pub struct RemovePost { #[cfg_attr(feature = "full", ts(export))] /// Mark a post as read. pub struct MarkPostAsRead { - pub post_ids: Vec, + pub post_id: PostId, pub read: bool, } @@ -162,7 +162,7 @@ pub struct MarkPostAsRead { #[cfg_attr(feature = "full", ts(export))] /// Hide a post from list views pub struct HidePost { - pub post_ids: Vec, + pub post_id: PostId, pub hide: bool, } diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs index db186247c..4b329d71b 100644 --- a/crates/api_common/src/utils.rs +++ b/crates/api_common/src/utils.rs @@ -59,7 +59,7 @@ use lemmy_utils::{ use moka::future::Cache; use regex::{escape, Regex, RegexSet}; use rosetta_i18n::{Language, LanguageId}; -use std::{collections::HashSet, sync::LazyLock}; +use std::sync::LazyLock; use tracing::warn; use url::{ParseError, Url}; use urlencoding::encode; @@ -154,7 +154,7 @@ pub async fn mark_post_as_read( post_id: PostId, pool: &mut DbPool<'_>, ) -> LemmyResult<()> { - PostRead::mark_as_read(pool, HashSet::from([post_id]), person_id) + PostRead::mark_as_read(pool, post_id, person_id) .await .with_lemmy_type(LemmyErrorType::CouldntMarkPostAsRead)?; Ok(()) diff --git a/crates/db_schema/src/impls/post.rs b/crates/db_schema/src/impls/post.rs index 43ccbc0a7..43bb0678c 100644 --- a/crates/db_schema/src/impls/post.rs +++ b/crates/db_schema/src/impls/post.rs @@ -38,7 +38,6 @@ use diesel::{ TextExpressionMethods, }; use diesel_async::RunQueryDsl; -use std::collections::HashSet; #[async_trait] impl Crud for Post { @@ -298,77 +297,67 @@ impl Saveable for PostSaved { impl PostRead { pub async fn mark_as_read( pool: &mut DbPool<'_>, - post_ids: HashSet, + post_id: PostId, person_id: PersonId, - ) -> Result { + ) -> Result { let conn = &mut get_conn(pool).await?; - let forms = post_ids - .into_iter() - .map(|post_id| PostReadForm { post_id, person_id }) - .collect::>(); + let form = PostReadForm { post_id, person_id }; + insert_into(post_read::table) - .values(forms) + .values(form) .on_conflict_do_nothing() - .execute(conn) + .get_result::(conn) .await } pub async fn mark_as_unread( pool: &mut DbPool<'_>, - post_id_: HashSet, + post_id_: PostId, person_id_: PersonId, ) -> Result { let conn = &mut get_conn(pool).await?; - diesel::delete( - post_read::table - .filter(post_read::post_id.eq_any(post_id_)) - .filter(post_read::person_id.eq(person_id_)), - ) - .execute(conn) - .await + let read_post = post_read::table + .filter(post_read::post_id.eq(post_id_)) + .filter(post_read::person_id.eq(person_id_)); + + diesel::delete(read_post).execute(conn).await } } impl PostHide { pub async fn hide( pool: &mut DbPool<'_>, - post_ids: HashSet, + post_id: PostId, person_id: PersonId, - ) -> Result { + ) -> Result { let conn = &mut get_conn(pool).await?; - let forms = post_ids - .into_iter() - .map(|post_id| PostHideForm { post_id, person_id }) - .collect::>(); + let form = PostHideForm { post_id, person_id }; insert_into(post_hide::table) - .values(forms) + .values(form) .on_conflict_do_nothing() - .execute(conn) + .get_result::(conn) .await } pub async fn unhide( pool: &mut DbPool<'_>, - post_id_: HashSet, + post_id_: PostId, person_id_: PersonId, ) -> Result { let conn = &mut get_conn(pool).await?; - diesel::delete( - post_hide::table - .filter(post_hide::post_id.eq_any(post_id_)) - .filter(post_hide::person_id.eq(person_id_)), - ) - .execute(conn) - .await + let hidden_post = post_hide::table + .filter(post_hide::post_id.eq(post_id_)) + .filter(post_hide::person_id.eq(person_id_)); + + diesel::delete(hidden_post).execute(conn).await } } #[cfg(test)] -#[allow(clippy::unwrap_used)] #[allow(clippy::indexing_slicing)] mod tests { @@ -391,24 +380,22 @@ mod tests { traits::{Crud, Likeable, Saveable}, utils::build_db_pool_for_tests, }; + use lemmy_utils::error::LemmyResult; use pretty_assertions::assert_eq; use serial_test::serial; - use std::collections::HashSet; use url::Url; #[tokio::test] #[serial] - async fn test_crud() { + async fn test_crud() -> LemmyResult<()> { let pool = &build_db_pool_for_tests().await; let pool = &mut pool.into(); - let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()) - .await - .unwrap(); + let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?; let new_person = PersonInsertForm::test_form(inserted_instance.id, "jim"); - let inserted_person = Person::create(pool, &new_person).await.unwrap(); + let inserted_person = Person::create(pool, &new_person).await?; let new_community = CommunityInsertForm::new( inserted_instance.id, @@ -417,21 +404,21 @@ mod tests { "pubkey".to_string(), ); - let inserted_community = Community::create(pool, &new_community).await.unwrap(); + let inserted_community = Community::create(pool, &new_community).await?; let new_post = PostInsertForm::new( "A test post".into(), inserted_person.id, inserted_community.id, ); - let inserted_post = Post::create(pool, &new_post).await.unwrap(); + let inserted_post = Post::create(pool, &new_post).await?; let new_post2 = PostInsertForm::new( "A test post 2".into(), inserted_person.id, inserted_community.id, ); - let inserted_post2 = Post::create(pool, &new_post2).await.unwrap(); + let inserted_post2 = Post::create(pool, &new_post2).await?; let expected_post = Post { id: inserted_post.id, @@ -451,9 +438,7 @@ mod tests { embed_description: None, embed_video_url: None, thumbnail_url: None, - ap_id: Url::parse(&format!("https://lemmy-alpha/post/{}", inserted_post.id)) - .unwrap() - .into(), + ap_id: Url::parse(&format!("https://lemmy-alpha/post/{}", inserted_post.id))?.into(), local: true, language_id: Default::default(), featured_community: false, @@ -468,7 +453,7 @@ mod tests { score: 1, }; - let inserted_post_like = PostLike::like(pool, &post_like_form).await.unwrap(); + let inserted_post_like = PostLike::like(pool, &post_like_form).await?; let expected_post_like = PostLike { post_id: inserted_post.id, @@ -483,7 +468,7 @@ mod tests { person_id: inserted_person.id, }; - let inserted_post_saved = PostSaved::save(pool, &post_saved_form).await.unwrap(); + let inserted_post_saved = PostSaved::save(pool, &post_saved_form).await?; let expected_post_saved = PostSaved { post_id: inserted_post.id, @@ -491,54 +476,44 @@ mod tests { published: inserted_post_saved.published, }; - // Post Read - let marked_as_read = PostRead::mark_as_read( - pool, - HashSet::from([inserted_post.id, inserted_post2.id]), - inserted_person.id, - ) - .await - .unwrap(); - assert_eq!(2, marked_as_read); + // Mark 2 posts as read + PostRead::mark_as_read(pool, inserted_post.id, inserted_person.id).await?; + PostRead::mark_as_read(pool, inserted_post2.id, inserted_person.id).await?; - let read_post = Post::read(pool, inserted_post.id).await.unwrap(); + let read_post = Post::read(pool, inserted_post.id).await?; let new_post_update = PostUpdateForm { name: Some("A test post".into()), ..Default::default() }; - let updated_post = Post::update(pool, inserted_post.id, &new_post_update) - .await - .unwrap(); + let updated_post = Post::update(pool, inserted_post.id, &new_post_update).await?; - let like_removed = PostLike::remove(pool, inserted_person.id, inserted_post.id) - .await - .unwrap(); + let like_removed = PostLike::remove(pool, inserted_person.id, inserted_post.id).await?; assert_eq!(1, like_removed); - let saved_removed = PostSaved::unsave(pool, &post_saved_form).await.unwrap(); + let saved_removed = PostSaved::unsave(pool, &post_saved_form).await?; assert_eq!(1, saved_removed); - let read_removed = PostRead::mark_as_unread( - pool, - HashSet::from([inserted_post.id, inserted_post2.id]), - inserted_person.id, - ) - .await - .unwrap(); - assert_eq!(2, read_removed); - let num_deleted = Post::delete(pool, inserted_post.id).await.unwrap() - + Post::delete(pool, inserted_post2.id).await.unwrap(); + // mark some posts as unread + let read_removed_1 = + PostRead::mark_as_unread(pool, inserted_post.id, inserted_person.id).await?; + assert_eq!(1, read_removed_1); + let read_removed_2 = + PostRead::mark_as_unread(pool, inserted_post2.id, inserted_person.id).await?; + assert_eq!(1, read_removed_2); + + let num_deleted = + Post::delete(pool, inserted_post.id).await? + Post::delete(pool, inserted_post2.id).await?; assert_eq!(2, num_deleted); - Community::delete(pool, inserted_community.id) - .await - .unwrap(); - Person::delete(pool, inserted_person.id).await.unwrap(); - Instance::delete(pool, inserted_instance.id).await.unwrap(); + Community::delete(pool, inserted_community.id).await?; + Person::delete(pool, inserted_person.id).await?; + Instance::delete(pool, inserted_instance.id).await?; assert_eq!(expected_post, read_post); assert_eq!(expected_post, inserted_post); assert_eq!(expected_post, updated_post); assert_eq!(expected_post_like, inserted_post_like); assert_eq!(expected_post_saved, inserted_post_saved); + + Ok(()) } } diff --git a/crates/db_views/src/post_view.rs b/crates/db_views/src/post_view.rs index 5dee59538..9eb3a4715 100644 --- a/crates/db_views/src/post_view.rs +++ b/crates/db_views/src/post_view.rs @@ -787,7 +787,7 @@ mod tests { use lemmy_utils::error::LemmyResult; use pretty_assertions::assert_eq; use serial_test::serial; - use std::{collections::HashSet, time::Duration}; + use std::time::Duration; use url::Url; const POST_WITH_ANOTHER_TITLE: &str = "Another title"; @@ -1622,7 +1622,7 @@ mod tests { // Mark a post as read PostRead::mark_as_read( pool, - HashSet::from([data.inserted_bot_post.id]), + data.inserted_bot_post.id, data.local_user_view.person.id, ) .await?; @@ -1664,7 +1664,7 @@ mod tests { // Mark a post as hidden PostHide::hide( pool, - HashSet::from([data.inserted_bot_post.id]), + data.inserted_bot_post.id, data.local_user_view.person.id, ) .await?;