mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-11-16 17:34:00 +00:00
Add user setting to auto-mark fetched posts as read. (#5160)
* Add user setting to auto-mark fetched posts as read.
- Rather than apps collecting up viewed posts ids, and sending many
mark as read requests, users can now turn this setting on, and any
results from /post/list will be auto-marked as read.
- Fixes #5144
* Adding list_post request option to auto-mark as read.
* Moving db_perf to before federation tests.
* Fixing lemmyerrortype import.
* Fixing ts_option.
* Fix clippy.
* Fix override logic.
* Revert "Fix override logic."
This reverts commit 923d7f0eca
.
* Changing name to mark_as_read
This commit is contained in:
parent
c4d864878f
commit
a9d6d4e6e0
17 changed files with 72 additions and 53 deletions
|
@ -138,17 +138,6 @@ steps:
|
||||||
- diff tmp.schema crates/db_schema/src/schema.rs
|
- diff tmp.schema crates/db_schema/src/schema.rs
|
||||||
when: *slow_check_paths
|
when: *slow_check_paths
|
||||||
|
|
||||||
check_db_perf_tool:
|
|
||||||
image: *rust_image
|
|
||||||
environment:
|
|
||||||
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
|
|
||||||
RUST_BACKTRACE: "1"
|
|
||||||
CARGO_HOME: .cargo_home
|
|
||||||
commands:
|
|
||||||
# same as scripts/db_perf.sh but without creating a new database server
|
|
||||||
- cargo run --package lemmy_db_perf -- --posts 10 --read-post-pages 1
|
|
||||||
when: *slow_check_paths
|
|
||||||
|
|
||||||
cargo_clippy:
|
cargo_clippy:
|
||||||
image: *rust_image
|
image: *rust_image
|
||||||
environment:
|
environment:
|
||||||
|
@ -221,6 +210,17 @@ steps:
|
||||||
- diff before.sqldump after.sqldump
|
- diff before.sqldump after.sqldump
|
||||||
when: *slow_check_paths
|
when: *slow_check_paths
|
||||||
|
|
||||||
|
check_db_perf_tool:
|
||||||
|
image: *rust_image
|
||||||
|
environment:
|
||||||
|
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
|
||||||
|
RUST_BACKTRACE: "1"
|
||||||
|
CARGO_HOME: .cargo_home
|
||||||
|
commands:
|
||||||
|
# same as scripts/db_perf.sh but without creating a new database server
|
||||||
|
- cargo run --package lemmy_db_perf -- --posts 10 --read-post-pages 1
|
||||||
|
when: *slow_check_paths
|
||||||
|
|
||||||
run_federation_tests:
|
run_federation_tests:
|
||||||
image: node:22-bookworm-slim
|
image: node:22-bookworm-slim
|
||||||
environment:
|
environment:
|
||||||
|
|
|
@ -143,6 +143,7 @@ pub async fn save_user_settings(
|
||||||
enable_animated_images: data.enable_animated_images,
|
enable_animated_images: data.enable_animated_images,
|
||||||
enable_private_messages: data.enable_private_messages,
|
enable_private_messages: data.enable_private_messages,
|
||||||
collapse_bot_comments: data.collapse_bot_comments,
|
collapse_bot_comments: data.collapse_bot_comments,
|
||||||
|
auto_mark_fetched_posts_as_read: data.auto_mark_fetched_posts_as_read,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,18 +5,12 @@ use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
post::{CreatePostLike, PostResponse},
|
post::{CreatePostLike, PostResponse},
|
||||||
send_activity::{ActivityChannel, SendActivityData},
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{
|
utils::{check_bot_account, check_community_user_action, check_local_vote_mode, VoteItem},
|
||||||
check_bot_account,
|
|
||||||
check_community_user_action,
|
|
||||||
check_local_vote_mode,
|
|
||||||
mark_post_as_read,
|
|
||||||
VoteItem,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
local_site::LocalSite,
|
local_site::LocalSite,
|
||||||
post::{PostLike, PostLikeForm},
|
post::{PostLike, PostLikeForm, PostRead},
|
||||||
},
|
},
|
||||||
traits::Likeable,
|
traits::Likeable,
|
||||||
};
|
};
|
||||||
|
@ -72,7 +66,8 @@ pub async fn like_post(
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntLikePost)?;
|
.with_lemmy_type(LemmyErrorType::CouldntLikePost)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
|
// Mark Post Read
|
||||||
|
PostRead::mark_as_read(&mut context.pool(), post_id, person_id).await?;
|
||||||
|
|
||||||
ActivityChannel::submit_activity(
|
ActivityChannel::submit_activity(
|
||||||
SendActivityData::LikePostOrComment {
|
SendActivityData::LikePostOrComment {
|
||||||
|
|
|
@ -5,7 +5,7 @@ use lemmy_api_common::{
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::source::post::PostRead;
|
use lemmy_db_schema::source::post::PostRead;
|
||||||
use lemmy_db_views::structs::{LocalUserView, PostView};
|
use lemmy_db_views::structs::{LocalUserView, PostView};
|
||||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
#[tracing::instrument(skip(context))]
|
||||||
pub async fn mark_post_as_read(
|
pub async fn mark_post_as_read(
|
||||||
|
@ -18,13 +18,9 @@ pub async fn mark_post_as_read(
|
||||||
|
|
||||||
// Mark the post as read / unread
|
// Mark the post as read / unread
|
||||||
if data.read {
|
if data.read {
|
||||||
PostRead::mark_as_read(&mut context.pool(), post_id, person_id)
|
PostRead::mark_as_read(&mut context.pool(), post_id, person_id).await?;
|
||||||
.await
|
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntMarkPostAsRead)?;
|
|
||||||
} else {
|
} else {
|
||||||
PostRead::mark_as_unread(&mut context.pool(), post_id, person_id)
|
PostRead::mark_as_unread(&mut context.pool(), post_id, person_id).await?;
|
||||||
.await
|
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntMarkPostAsRead)?;
|
|
||||||
}
|
}
|
||||||
let post_view = PostView::read(
|
let post_view = PostView::read(
|
||||||
&mut context.pool(),
|
&mut context.pool(),
|
||||||
|
|
|
@ -2,10 +2,9 @@ use actix_web::web::{Data, Json};
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
post::{PostResponse, SavePost},
|
post::{PostResponse, SavePost},
|
||||||
utils::mark_post_as_read,
|
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::post::{PostSaved, PostSavedForm},
|
source::post::{PostRead, PostSaved, PostSavedForm},
|
||||||
traits::Saveable,
|
traits::Saveable,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{LocalUserView, PostView};
|
use lemmy_db_views::structs::{LocalUserView, PostView};
|
||||||
|
@ -42,7 +41,7 @@ pub async fn save_post(
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
|
PostRead::mark_as_read(&mut context.pool(), post_id, person_id).await?;
|
||||||
|
|
||||||
Ok(Json(PostResponse { post_view }))
|
Ok(Json(PostResponse { post_view }))
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,6 +178,9 @@ pub struct SaveUserSettings {
|
||||||
pub show_downvotes: Option<bool>,
|
pub show_downvotes: Option<bool>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub show_upvote_percentage: Option<bool>,
|
pub show_upvote_percentage: Option<bool>,
|
||||||
|
/// Whether to automatically mark fetched posts as read.
|
||||||
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
|
pub auto_mark_fetched_posts_as_read: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)]
|
||||||
|
|
|
@ -108,6 +108,9 @@ pub struct GetPosts {
|
||||||
/// If true, then show the nsfw posts (even if your user setting is to hide them)
|
/// If true, then show the nsfw posts (even if your user setting is to hide them)
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub show_nsfw: Option<bool>,
|
pub show_nsfw: Option<bool>,
|
||||||
|
/// Whether to automatically mark fetched posts as read.
|
||||||
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
|
pub mark_as_read: Option<bool>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
/// If true, then only show posts with no comments
|
/// If true, then only show posts with no comments
|
||||||
pub no_comments_only: Option<bool>,
|
pub no_comments_only: Option<bool>,
|
||||||
|
|
|
@ -28,7 +28,7 @@ use lemmy_db_schema::{
|
||||||
password_reset_request::PasswordResetRequest,
|
password_reset_request::PasswordResetRequest,
|
||||||
person::{Person, PersonUpdateForm},
|
person::{Person, PersonUpdateForm},
|
||||||
person_block::PersonBlock,
|
person_block::PersonBlock,
|
||||||
post::{Post, PostLike, PostRead},
|
post::{Post, PostLike},
|
||||||
registration_application::RegistrationApplication,
|
registration_application::RegistrationApplication,
|
||||||
site::Site,
|
site::Site,
|
||||||
},
|
},
|
||||||
|
@ -141,19 +141,6 @@ pub fn is_top_mod(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Marks a post as read for a given person.
|
|
||||||
#[tracing::instrument(skip_all)]
|
|
||||||
pub async fn mark_post_as_read(
|
|
||||||
person_id: PersonId,
|
|
||||||
post_id: PostId,
|
|
||||||
pool: &mut DbPool<'_>,
|
|
||||||
) -> LemmyResult<()> {
|
|
||||||
PostRead::mark_as_read(pool, post_id, person_id)
|
|
||||||
.await
|
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntMarkPostAsRead)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Updates the read comment count for a post. Usually done when reading or creating a new comment.
|
/// Updates the read comment count for a post. Usually done when reading or creating a new comment.
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn update_read_comments(
|
pub async fn update_read_comments(
|
||||||
|
|
|
@ -12,7 +12,6 @@ use lemmy_api_common::{
|
||||||
get_url_blocklist,
|
get_url_blocklist,
|
||||||
honeypot_check,
|
honeypot_check,
|
||||||
local_site_to_slur_regex,
|
local_site_to_slur_regex,
|
||||||
mark_post_as_read,
|
|
||||||
process_markdown_opt,
|
process_markdown_opt,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -21,7 +20,7 @@ use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
community::Community,
|
community::Community,
|
||||||
local_site::LocalSite,
|
local_site::LocalSite,
|
||||||
post::{Post, PostInsertForm, PostLike, PostLikeForm},
|
post::{Post, PostInsertForm, PostLike, PostLikeForm, PostRead},
|
||||||
},
|
},
|
||||||
traits::{Crud, Likeable},
|
traits::{Crud, Likeable},
|
||||||
utils::diesel_url_create,
|
utils::diesel_url_create,
|
||||||
|
@ -153,7 +152,7 @@ pub async fn create_post(
|
||||||
.await
|
.await
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntLikePost)?;
|
.with_lemmy_type(LemmyErrorType::CouldntLikePost)?;
|
||||||
|
|
||||||
mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
|
PostRead::mark_as_read(&mut context.pool(), post_id, person_id).await?;
|
||||||
|
|
||||||
build_post_response(&context, community_id, local_user_view, post_id).await
|
build_post_response(&context, community_id, local_user_view, post_id).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,13 @@ use actix_web::web::{Data, Json, Query};
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
post::{GetPost, GetPostResponse},
|
post::{GetPost, GetPostResponse},
|
||||||
utils::{check_private_instance, is_mod_or_admin_opt, mark_post_as_read, update_read_comments},
|
utils::{check_private_instance, is_mod_or_admin_opt, update_read_comments},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{comment::Comment, post::Post},
|
source::{
|
||||||
|
comment::Comment,
|
||||||
|
post::{Post, PostRead},
|
||||||
|
},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::{
|
use lemmy_db_views::{
|
||||||
|
@ -62,7 +65,7 @@ pub async fn get_post(
|
||||||
|
|
||||||
let post_id = post_view.post.id;
|
let post_id = post_view.post.id;
|
||||||
if let Some(person_id) = person_id {
|
if let Some(person_id) = person_id {
|
||||||
mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
|
PostRead::mark_as_read(&mut context.pool(), post_id, person_id).await?;
|
||||||
|
|
||||||
update_read_comments(
|
update_read_comments(
|
||||||
person_id,
|
person_id,
|
||||||
|
|
|
@ -10,7 +10,10 @@ use lemmy_api_common::{
|
||||||
post::{GetPosts, GetPostsResponse},
|
post::{GetPosts, GetPostsResponse},
|
||||||
utils::{check_conflicting_like_filters, check_private_instance},
|
utils::{check_conflicting_like_filters, check_private_instance},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::source::community::Community;
|
use lemmy_db_schema::{
|
||||||
|
newtypes::PostId,
|
||||||
|
source::{community::Community, post::PostRead},
|
||||||
|
};
|
||||||
use lemmy_db_views::{
|
use lemmy_db_views::{
|
||||||
post_view::PostQuery,
|
post_view::PostQuery,
|
||||||
structs::{LocalUserView, PaginationCursor, SiteView},
|
structs::{LocalUserView, PaginationCursor, SiteView},
|
||||||
|
@ -90,6 +93,20 @@ pub async fn list_posts(
|
||||||
.await
|
.await
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntGetPosts)?;
|
.with_lemmy_type(LemmyErrorType::CouldntGetPosts)?;
|
||||||
|
|
||||||
|
// If in their user settings (or as part of the API request), auto-mark fetched posts as read
|
||||||
|
if let Some(local_user) = local_user {
|
||||||
|
if data
|
||||||
|
.mark_as_read
|
||||||
|
.unwrap_or(local_user.auto_mark_fetched_posts_as_read)
|
||||||
|
{
|
||||||
|
let post_ids = posts.iter().map(|p| p.post.id).collect::<Vec<PostId>>();
|
||||||
|
// 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?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if this page wasn't empty, then there is a next page after the last post on this page
|
// if this page wasn't empty, then there is a next page after the last post on this page
|
||||||
let next_page = posts.last().map(PaginationCursor::after_post);
|
let next_page = posts.last().map(PaginationCursor::after_post);
|
||||||
Ok(Json(GetPostsResponse { posts, next_page }))
|
Ok(Json(GetPostsResponse { posts, next_page }))
|
||||||
|
|
|
@ -42,6 +42,7 @@ use diesel::{
|
||||||
TextExpressionMethods,
|
TextExpressionMethods,
|
||||||
};
|
};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Crud for Post {
|
impl Crud for Post {
|
||||||
|
@ -336,7 +337,7 @@ impl PostRead {
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
post_id: PostId,
|
post_id: PostId,
|
||||||
person_id: PersonId,
|
person_id: PersonId,
|
||||||
) -> Result<usize, Error> {
|
) -> LemmyResult<usize> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
|
||||||
let form = (
|
let form = (
|
||||||
|
@ -351,13 +352,14 @@ impl PostRead {
|
||||||
.set(form)
|
.set(form)
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.await
|
.await
|
||||||
|
.with_lemmy_type(LemmyErrorType::CouldntMarkPostAsRead)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn mark_as_unread(
|
pub async fn mark_as_unread(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
post_id_: PostId,
|
post_id_: PostId,
|
||||||
person_id_: PersonId,
|
person_id_: PersonId,
|
||||||
) -> Result<uplete::Count, Error> {
|
) -> LemmyResult<uplete::Count> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
|
||||||
uplete::new(
|
uplete::new(
|
||||||
|
@ -368,6 +370,7 @@ impl PostRead {
|
||||||
.set_null(post_actions::read)
|
.set_null(post_actions::read)
|
||||||
.get_result(conn)
|
.get_result(conn)
|
||||||
.await
|
.await
|
||||||
|
.with_lemmy_type(LemmyErrorType::CouldntMarkPostAsRead)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -455,6 +455,7 @@ diesel::table! {
|
||||||
enable_private_messages -> Bool,
|
enable_private_messages -> Bool,
|
||||||
collapse_bot_comments -> Bool,
|
collapse_bot_comments -> Bool,
|
||||||
default_comment_sort_type -> CommentSortTypeEnum,
|
default_comment_sort_type -> CommentSortTypeEnum,
|
||||||
|
auto_mark_fetched_posts_as_read -> Bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,8 @@ pub struct LocalUser {
|
||||||
/// Whether to auto-collapse bot comments.
|
/// Whether to auto-collapse bot comments.
|
||||||
pub collapse_bot_comments: bool,
|
pub collapse_bot_comments: bool,
|
||||||
pub default_comment_sort_type: CommentSortType,
|
pub default_comment_sort_type: CommentSortType,
|
||||||
|
/// Whether to automatically mark fetched posts as read.
|
||||||
|
pub auto_mark_fetched_posts_as_read: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, derive_new::new)]
|
#[derive(Clone, derive_new::new)]
|
||||||
|
@ -124,6 +126,8 @@ pub struct LocalUserInsertForm {
|
||||||
pub collapse_bot_comments: Option<bool>,
|
pub collapse_bot_comments: Option<bool>,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub default_comment_sort_type: Option<CommentSortType>,
|
pub default_comment_sort_type: Option<CommentSortType>,
|
||||||
|
#[new(default)]
|
||||||
|
pub auto_mark_fetched_posts_as_read: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
|
@ -155,4 +159,5 @@ pub struct LocalUserUpdateForm {
|
||||||
pub enable_private_messages: Option<bool>,
|
pub enable_private_messages: Option<bool>,
|
||||||
pub collapse_bot_comments: Option<bool>,
|
pub collapse_bot_comments: Option<bool>,
|
||||||
pub default_comment_sort_type: Option<CommentSortType>,
|
pub default_comment_sort_type: Option<CommentSortType>,
|
||||||
|
pub auto_mark_fetched_posts_as_read: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -242,6 +242,7 @@ mod tests {
|
||||||
enable_animated_images: inserted_sara_local_user.enable_animated_images,
|
enable_animated_images: inserted_sara_local_user.enable_animated_images,
|
||||||
enable_private_messages: inserted_sara_local_user.enable_private_messages,
|
enable_private_messages: inserted_sara_local_user.enable_private_messages,
|
||||||
collapse_bot_comments: inserted_sara_local_user.collapse_bot_comments,
|
collapse_bot_comments: inserted_sara_local_user.collapse_bot_comments,
|
||||||
|
auto_mark_fetched_posts_as_read: false,
|
||||||
},
|
},
|
||||||
creator: Person {
|
creator: Person {
|
||||||
id: inserted_sara_person.id,
|
id: inserted_sara_person.id,
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
ALTER TABLE local_user
|
||||||
|
DROP COLUMN auto_mark_fetched_posts_as_read;
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
ALTER TABLE local_user
|
||||||
|
ADD COLUMN auto_mark_fetched_posts_as_read boolean DEFAULT FALSE NOT NULL;
|
||||||
|
|
Loading…
Reference in a new issue