From 7f4e26e29e3b9ffd0da8b7ddbf819c23de604103 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Thu, 14 Nov 2024 09:03:39 -0500 Subject: [PATCH] Add ability to mark multiple posts as read. (#5178) * Removing a few SuccessResponses for PostHide and MarkPostAsRead. - This also removes the pointless multiple post_ids. These can be done as individual calls on the front end anyway. - Fixes #4755 * Fixing federation tests. * Upgrading lemmy-js-client deps. * Add ability to mark several posts as read. Context: - https://github.com/LemmyNet/lemmy/pull/5043 - https://github.com/LemmyNet/lemmy/issues/4755 - https://github.com/LemmyNet/lemmy/pull/5160 * Fix ntfy to notify on success builds also. * Addressing PR comments. --- .woodpecker.yml | 6 ++-- crates/api/src/post/mark_many_read.rs | 24 ++++++++++++++ crates/api/src/post/mod.rs | 1 + crates/api_common/src/post.rs | 9 ++++++ crates/apub/src/api/list_posts.rs | 5 +-- crates/db_schema/src/impls/post.rs | 45 ++++++++++++++++++--------- src/api_routes_http.rs | 2 ++ 7 files changed, 70 insertions(+), 22 deletions(-) create mode 100644 crates/api/src/post/mark_many_read.rs diff --git a/.woodpecker.yml b/.woodpecker.yml index 1a96c2c66..c9ba830dd 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -290,14 +290,14 @@ steps: when: - event: tag - notify_on_failure: + notify_on_build: image: alpine:3 commands: - apk add curl - - "curl -d'Lemmy CI build failed: ${CI_PIPELINE_URL}' ntfy.sh/lemmy_drone_ci" + - "curl -d'Lemmy CI build ${CI_PIPELINE_STATUS}: ${CI_PIPELINE_URL}' ntfy.sh/lemmy_drone_ci" when: - event: [pull_request, tag] - status: failure + status: [failure, success] notify_on_tag_deploy: image: alpine:3 diff --git a/crates/api/src/post/mark_many_read.rs b/crates/api/src/post/mark_many_read.rs new file mode 100644 index 000000000..82c2c0b06 --- /dev/null +++ b/crates/api/src/post/mark_many_read.rs @@ -0,0 +1,24 @@ +use actix_web::web::{Data, Json}; +use lemmy_api_common::{context::LemmyContext, post::MarkManyPostsAsRead, SuccessResponse}; +use lemmy_db_schema::source::post::PostRead; +use lemmy_db_views::structs::LocalUserView; +use lemmy_utils::error::{LemmyErrorType, LemmyResult, MAX_API_PARAM_ELEMENTS}; + +#[tracing::instrument(skip(context))] +pub async fn mark_posts_as_read( + data: Json, + context: Data, + local_user_view: LocalUserView, +) -> LemmyResult> { + let post_ids = &data.post_ids; + if post_ids.len() > MAX_API_PARAM_ELEMENTS { + Err(LemmyErrorType::TooManyItems)?; + } + + let person_id = local_user_view.person.id; + + // Mark the posts as read + PostRead::mark_many_as_read(&mut context.pool(), post_ids, person_id).await?; + + Ok(Json(SuccessResponse::default())) +} diff --git a/crates/api/src/post/mod.rs b/crates/api/src/post/mod.rs index 7287010f7..97410f097 100644 --- a/crates/api/src/post/mod.rs +++ b/crates/api/src/post/mod.rs @@ -4,5 +4,6 @@ pub mod hide; pub mod like; pub mod list_post_likes; pub mod lock; +pub mod mark_many_read; pub mod mark_read; pub mod save; diff --git a/crates/api_common/src/post.rs b/crates/api_common/src/post.rs index 39e87e683..405de3a92 100644 --- a/crates/api_common/src/post.rs +++ b/crates/api_common/src/post.rs @@ -200,6 +200,15 @@ pub struct MarkPostAsRead { pub read: bool, } +#[skip_serializing_none] +#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "full", derive(TS))] +#[cfg_attr(feature = "full", ts(export))] +/// Mark several posts as read. +pub struct MarkManyPostsAsRead { + pub post_ids: Vec, +} + #[skip_serializing_none] #[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)] #[cfg_attr(feature = "full", derive(TS))] diff --git a/crates/apub/src/api/list_posts.rs b/crates/apub/src/api/list_posts.rs index dd80dcbcb..63e737fdd 100644 --- a/crates/apub/src/api/list_posts.rs +++ b/crates/apub/src/api/list_posts.rs @@ -100,10 +100,7 @@ pub async fn list_posts( .unwrap_or(local_user.auto_mark_fetched_posts_as_read) { let post_ids = posts.iter().map(|p| p.post.id).collect::>(); - // TODO get rid of this in the next pr - for post_id in post_ids { - PostRead::mark_as_read(&mut context.pool(), post_id, local_user.person_id).await?; - } + PostRead::mark_many_as_read(&mut context.pool(), &post_ids, local_user.person_id).await?; } } diff --git a/crates/db_schema/src/impls/post.rs b/crates/db_schema/src/impls/post.rs index c8b34d3bc..e60cd3a2b 100644 --- a/crates/db_schema/src/impls/post.rs +++ b/crates/db_schema/src/impls/post.rs @@ -338,21 +338,7 @@ impl PostRead { post_id: PostId, person_id: PersonId, ) -> LemmyResult { - let conn = &mut get_conn(pool).await?; - - let form = ( - &PostReadForm { post_id, person_id }, - post_actions::read.eq(now().nullable()), - ); - - insert_into(post_actions::table) - .values(form) - .on_conflict((post_actions::person_id, post_actions::post_id)) - .do_update() - .set(form) - .execute(conn) - .await - .with_lemmy_type(LemmyErrorType::CouldntMarkPostAsRead) + Self::mark_many_as_read(pool, &[post_id], person_id).await } pub async fn mark_as_unread( @@ -372,6 +358,35 @@ impl PostRead { .await .with_lemmy_type(LemmyErrorType::CouldntMarkPostAsRead) } + + pub async fn mark_many_as_read( + pool: &mut DbPool<'_>, + post_ids: &[PostId], + person_id: PersonId, + ) -> LemmyResult { + let conn = &mut get_conn(pool).await?; + + let forms = post_ids + .iter() + .map(|post_id| { + ( + PostReadForm { + post_id: *post_id, + person_id, + }, + post_actions::read.eq(now().nullable()), + ) + }) + .collect::>(); + insert_into(post_actions::table) + .values(forms) + .on_conflict((post_actions::person_id, post_actions::post_id)) + .do_update() + .set(post_actions::read.eq(now().nullable())) + .execute(conn) + .await + .with_lemmy_type(LemmyErrorType::CouldntMarkPostAsRead) + } } impl PostHide { diff --git a/src/api_routes_http.rs b/src/api_routes_http.rs index fd65e0671..2f431419c 100644 --- a/src/api_routes_http.rs +++ b/src/api_routes_http.rs @@ -60,6 +60,7 @@ use lemmy_api::{ like::like_post, list_post_likes::list_post_likes, lock::lock_post, + mark_many_read::mark_posts_as_read, mark_read::mark_post_as_read, save::save_post, }, @@ -239,6 +240,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) { .route("/delete", web::post().to(delete_post)) .route("/remove", web::post().to(remove_post)) .route("/mark_as_read", web::post().to(mark_post_as_read)) + .route("/mark_many_as_read", web::post().to(mark_posts_as_read)) .route("/hide", web::post().to(hide_post)) .route("/lock", web::post().to(lock_post)) .route("/feature", web::post().to(feature_post))