From 15f02f00a95d3d31ee1ec91c089e370daef7b59f Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 13 Mar 2024 12:10:58 -0400 Subject: [PATCH] Add a vote_display_mode local_user setting. (#4450) * Add a vote_display_mode local_user setting. - Fixes #4449 * Changing HideDownvotes to Score. * Adding ScoreAndDownvote display mode. * Adding upvote and downvote mode. * Extracting vote_display_mode to another table. * Fixing fmt. * Remove published and updated columns. --------- Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com> --- crates/api/src/local_user/save_settings.rs | 11 ++++ crates/api_common/src/person.rs | 8 ++- crates/api_crud/src/user/create.rs | 2 + crates/apub/src/api/user_settings_backup.rs | 24 ++++++++ crates/db_schema/src/impls/local_user.rs | 7 +++ .../src/impls/local_user_vote_display_mode.rs | 57 +++++++++++++++++++ crates/db_schema/src/impls/mod.rs | 1 + crates/db_schema/src/schema.rs | 12 ++++ crates/db_schema/src/source/local_user.rs | 2 + .../source/local_user_vote_display_mode.rs | 51 +++++++++++++++++ crates/db_schema/src/source/mod.rs | 1 + crates/db_views/src/comment_report_view.rs | 2 + crates/db_views/src/comment_view.rs | 2 + crates/db_views/src/local_user_view.rs | 9 ++- crates/db_views/src/post_report_view.rs | 2 + crates/db_views/src/post_view.rs | 3 + crates/db_views/src/structs.rs | 2 + .../down.sql | 2 + .../up.sql | 18 ++++++ 19 files changed, 211 insertions(+), 5 deletions(-) create mode 100644 crates/db_schema/src/impls/local_user_vote_display_mode.rs create mode 100644 crates/db_schema/src/source/local_user_vote_display_mode.rs create mode 100644 migrations/2024-02-12-211114_add_vote_display_mode_setting/down.sql create mode 100644 migrations/2024-02-12-211114_add_vote_display_mode_setting/up.sql diff --git a/crates/api/src/local_user/save_settings.rs b/crates/api/src/local_user/save_settings.rs index 79b95133e..d918bdc00 100644 --- a/crates/api/src/local_user/save_settings.rs +++ b/crates/api/src/local_user/save_settings.rs @@ -14,6 +14,7 @@ use lemmy_db_schema::{ source::{ actor_language::LocalUserLanguage, local_user::{LocalUser, LocalUserUpdateForm}, + local_user_vote_display_mode::{LocalUserVoteDisplayMode, LocalUserVoteDisplayModeUpdateForm}, person::{Person, PersonUpdateForm}, }, traits::Crud, @@ -136,5 +137,15 @@ pub async fn save_user_settings( .await .ok(); + // Update the vote display modes + let vote_display_modes_form = LocalUserVoteDisplayModeUpdateForm { + score: data.show_scores, + upvotes: data.show_upvotes, + downvotes: data.show_downvotes, + upvote_percentage: data.show_upvote_percentage, + }; + LocalUserVoteDisplayMode::update(&mut context.pool(), local_user_id, &vote_display_modes_form) + .await?; + Ok(Json(SuccessResponse::default())) } diff --git a/crates/api_common/src/person.rs b/crates/api_common/src/person.rs index c5011d2f9..7af966164 100644 --- a/crates/api_common/src/person.rs +++ b/crates/api_common/src/person.rs @@ -86,8 +86,6 @@ pub struct SaveUserSettings { pub show_nsfw: Option, pub blur_nsfw: Option, pub auto_expand: Option, - /// Show post and comment scores. - pub show_scores: Option, /// Your user's theme. pub theme: Option, pub default_sort_type: Option, @@ -122,6 +120,7 @@ pub struct SaveUserSettings { pub open_links_in_new_tab: Option, /// Enable infinite scroll pub infinite_scroll_enabled: Option, + /// A post-view mode that changes how multiple post listings look. pub post_listing_mode: Option, /// Whether to allow keyboard navigation (for browsing and interacting with posts and comments). pub enable_keyboard_navigation: Option, @@ -129,6 +128,11 @@ pub struct SaveUserSettings { pub enable_animated_images: Option, /// Whether to auto-collapse bot comments. pub collapse_bot_comments: Option, + /// Some vote display mode settings + pub show_scores: Option, + pub show_upvotes: Option, + pub show_downvotes: Option, + pub show_upvote_percentage: Option, } #[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)] diff --git a/crates/api_crud/src/user/create.rs b/crates/api_crud/src/user/create.rs index 50df1edbf..29e16cb20 100644 --- a/crates/api_crud/src/user/create.rs +++ b/crates/api_crud/src/user/create.rs @@ -21,6 +21,7 @@ use lemmy_db_schema::{ source::{ captcha_answer::{CaptchaAnswer, CheckCaptchaAnswer}, local_user::{LocalUser, LocalUserInsertForm}, + local_user_vote_display_mode::LocalUserVoteDisplayMode, person::{Person, PersonInsertForm}, registration_application::{RegistrationApplication, RegistrationApplicationInsertForm}, }, @@ -183,6 +184,7 @@ pub async fn register( if local_site.require_email_verification { let local_user_view = LocalUserView { local_user: inserted_local_user, + local_user_vote_display_mode: LocalUserVoteDisplayMode::default(), person: inserted_person, counts: PersonAggregates::default(), }; diff --git a/crates/apub/src/api/user_settings_backup.rs b/crates/apub/src/api/user_settings_backup.rs index ebe2940d4..a2e6c55ac 100644 --- a/crates/apub/src/api/user_settings_backup.rs +++ b/crates/apub/src/api/user_settings_backup.rs @@ -17,6 +17,7 @@ use lemmy_db_schema::{ instance::Instance, instance_block::{InstanceBlock, InstanceBlockForm}, local_user::{LocalUser, LocalUserUpdateForm}, + local_user_vote_display_mode::{LocalUserVoteDisplayMode, LocalUserVoteDisplayModeUpdateForm}, person::{Person, PersonUpdateForm}, person_block::{PersonBlock, PersonBlockForm}, post::{PostSaved, PostSavedForm}, @@ -50,6 +51,7 @@ pub struct UserSettingsBackup { // TODO: might be worth making a separate struct for settings backup, to avoid breakage in case // fields are renamed, and to avoid storing unnecessary fields like person_id or email pub settings: Option, + pub vote_display_mode_settings: Option, #[serde(default)] pub followed_communities: Vec>, #[serde(default)] @@ -80,6 +82,7 @@ pub async fn export_settings( matrix_id: local_user_view.person.matrix_user_id, bot_account: local_user_view.person.bot_account.into(), settings: Some(local_user_view.local_user), + vote_display_mode_settings: Some(local_user_view.local_user_vote_display_mode), followed_communities: vec_into(lists.followed_communities), blocked_communities: vec_into(lists.blocked_communities), blocked_instances: lists.blocked_instances, @@ -132,6 +135,27 @@ pub async fn import_settings( ) .await?; + // Update the vote display mode settings + let vote_display_mode_form = LocalUserVoteDisplayModeUpdateForm { + score: data.vote_display_mode_settings.as_ref().map(|s| s.score), + upvotes: data.vote_display_mode_settings.as_ref().map(|s| s.upvotes), + downvotes: data + .vote_display_mode_settings + .as_ref() + .map(|s| s.downvotes), + upvote_percentage: data + .vote_display_mode_settings + .as_ref() + .map(|s| s.upvote_percentage), + }; + + LocalUserVoteDisplayMode::update( + &mut context.pool(), + local_user_view.local_user.id, + &vote_display_mode_form, + ) + .await?; + let url_count = data.followed_communities.len() + data.blocked_communities.len() + data.blocked_users.len() diff --git a/crates/db_schema/src/impls/local_user.rs b/crates/db_schema/src/impls/local_user.rs index 14da24bae..14ae65469 100644 --- a/crates/db_schema/src/impls/local_user.rs +++ b/crates/db_schema/src/impls/local_user.rs @@ -4,6 +4,7 @@ use crate::{ source::{ actor_language::{LocalUserLanguage, SiteLanguage}, local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm}, + local_user_vote_display_mode::{LocalUserVoteDisplayMode, LocalUserVoteDisplayModeInsertForm}, }, traits::Crud, utils::{ @@ -211,6 +212,12 @@ impl Crud for LocalUser { LocalUserLanguage::update(pool, vec![], local_user_.id).await?; } + // Create their vote_display_modes + let vote_display_mode_form = LocalUserVoteDisplayModeInsertForm::builder() + .local_user_id(local_user_.id) + .build(); + LocalUserVoteDisplayMode::create(pool, &vote_display_mode_form).await?; + Ok(local_user_) } async fn update( diff --git a/crates/db_schema/src/impls/local_user_vote_display_mode.rs b/crates/db_schema/src/impls/local_user_vote_display_mode.rs new file mode 100644 index 000000000..4458fc1b6 --- /dev/null +++ b/crates/db_schema/src/impls/local_user_vote_display_mode.rs @@ -0,0 +1,57 @@ +use crate::{ + newtypes::LocalUserId, + schema::local_user_vote_display_mode, + source::local_user_vote_display_mode::{ + LocalUserVoteDisplayMode, + LocalUserVoteDisplayModeInsertForm, + LocalUserVoteDisplayModeUpdateForm, + }, + utils::{get_conn, DbPool}, +}; +use diesel::{dsl::insert_into, result::Error, QueryDsl}; +use diesel_async::RunQueryDsl; + +impl LocalUserVoteDisplayMode { + pub async fn read(pool: &mut DbPool<'_>) -> Result { + let conn = &mut get_conn(pool).await?; + local_user_vote_display_mode::table + .first::(conn) + .await + } + + pub async fn create( + pool: &mut DbPool<'_>, + form: &LocalUserVoteDisplayModeInsertForm, + ) -> Result { + let conn = &mut get_conn(pool).await?; + insert_into(local_user_vote_display_mode::table) + .values(form) + .get_result::(conn) + .await + } + pub async fn update( + pool: &mut DbPool<'_>, + local_user_id: LocalUserId, + form: &LocalUserVoteDisplayModeUpdateForm, + ) -> Result<(), Error> { + // avoid error "There are no changes to save. This query cannot be built" + if form.is_empty() { + return Ok(()); + } + let conn = &mut get_conn(pool).await?; + diesel::update(local_user_vote_display_mode::table.find(local_user_id)) + .set(form) + .get_result::(conn) + .await?; + Ok(()) + } +} + +impl LocalUserVoteDisplayModeUpdateForm { + fn is_empty(&self) -> bool { + self.score.is_none() + && self.upvotes.is_none() + && self.downvotes.is_none() + && self.upvote_percentage.is_none() + } +} diff --git a/crates/db_schema/src/impls/mod.rs b/crates/db_schema/src/impls/mod.rs index 6d4549b14..711a6c4e6 100644 --- a/crates/db_schema/src/impls/mod.rs +++ b/crates/db_schema/src/impls/mod.rs @@ -18,6 +18,7 @@ pub mod language; pub mod local_site; pub mod local_site_rate_limit; pub mod local_user; +pub mod local_user_vote_display_mode; pub mod login_token; pub mod moderator; pub mod password_reset_request; diff --git a/crates/db_schema/src/schema.rs b/crates/db_schema/src/schema.rs index 632f7d0bb..a61b2d24f 100644 --- a/crates/db_schema/src/schema.rs +++ b/crates/db_schema/src/schema.rs @@ -454,6 +454,16 @@ diesel::table! { } } +diesel::table! { + local_user_vote_display_mode (local_user_id) { + local_user_id -> Int4, + score -> Bool, + upvotes -> Bool, + downvotes -> Bool, + upvote_percentage -> Bool, + } +} + diesel::table! { login_token (token) { token -> Text, @@ -961,6 +971,7 @@ diesel::joinable!(local_site_rate_limit -> local_site (local_site_id)); diesel::joinable!(local_user -> person (person_id)); diesel::joinable!(local_user_language -> language (language_id)); diesel::joinable!(local_user_language -> local_user (local_user_id)); +diesel::joinable!(local_user_vote_display_mode -> local_user (local_user_id)); diesel::joinable!(login_token -> local_user (user_id)); diesel::joinable!(mod_add_community -> community (community_id)); diesel::joinable!(mod_ban_from_community -> community (community_id)); @@ -1043,6 +1054,7 @@ diesel::allow_tables_to_appear_in_same_query!( local_site_rate_limit, local_user, local_user_language, + local_user_vote_display_mode, login_token, mod_add, mod_add_community, diff --git a/crates/db_schema/src/source/local_user.rs b/crates/db_schema/src/source/local_user.rs index 97a0921da..f159232f7 100644 --- a/crates/db_schema/src/source/local_user.rs +++ b/crates/db_schema/src/source/local_user.rs @@ -36,6 +36,7 @@ pub struct LocalUser { pub show_avatars: bool, pub send_notifications_to_email: bool, /// Whether to show comment / post scores. + // TODO now that there is a vote_display_mode, this can be gotten rid of in future releases. pub show_scores: bool, /// Whether to show bot accounts. pub show_bot_accounts: bool, @@ -55,6 +56,7 @@ pub struct LocalUser { pub infinite_scroll_enabled: bool, /// Whether the person is an admin. pub admin: bool, + /// A post-view mode that changes how multiple post listings look. pub post_listing_mode: PostListingMode, pub totp_2fa_enabled: bool, /// Whether to allow keyboard navigation (for browsing and interacting with posts and comments). diff --git a/crates/db_schema/src/source/local_user_vote_display_mode.rs b/crates/db_schema/src/source/local_user_vote_display_mode.rs new file mode 100644 index 000000000..314d99e4a --- /dev/null +++ b/crates/db_schema/src/source/local_user_vote_display_mode.rs @@ -0,0 +1,51 @@ +use crate::newtypes::LocalUserId; +#[cfg(feature = "full")] +use crate::schema::local_user_vote_display_mode; +use serde::{Deserialize, Serialize}; +use serde_with::skip_serializing_none; +#[cfg(feature = "full")] +use ts_rs::TS; +use typed_builder::TypedBuilder; + +#[skip_serializing_none] +#[derive(PartialEq, Eq, Debug, Clone, Default, Serialize, Deserialize)] +#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))] +#[cfg_attr(feature = "full", diesel(table_name = local_user_vote_display_mode))] +#[cfg_attr(feature = "full", diesel(primary_key(local_user_id)))] +#[cfg_attr( + feature = "full", + diesel(belongs_to(crate::source::local_site::LocalUser)) +)] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// The vote display settings for your user. +pub struct LocalUserVoteDisplayMode { + pub local_user_id: LocalUserId, + pub score: bool, + pub upvotes: bool, + pub downvotes: bool, + pub upvote_percentage: bool, +} + +#[derive(Clone, TypedBuilder)] +#[builder(field_defaults(default))] +#[cfg_attr(feature = "full", derive(Insertable))] +#[cfg_attr(feature = "full", diesel(table_name = local_user_vote_display_mode))] +pub struct LocalUserVoteDisplayModeInsertForm { + #[builder(!default)] + pub local_user_id: LocalUserId, + pub score: Option, + pub upvotes: Option, + pub downvotes: Option, + pub upvote_percentage: Option, +} + +#[derive(Clone, Default)] +#[cfg_attr(feature = "full", derive(AsChangeset))] +#[cfg_attr(feature = "full", diesel(table_name = local_user_vote_display_mode))] +pub struct LocalUserVoteDisplayModeUpdateForm { + pub score: Option, + pub upvotes: Option, + pub downvotes: Option, + pub upvote_percentage: Option, +} diff --git a/crates/db_schema/src/source/mod.rs b/crates/db_schema/src/source/mod.rs index bccb7c8ce..ab82a114c 100644 --- a/crates/db_schema/src/source/mod.rs +++ b/crates/db_schema/src/source/mod.rs @@ -23,6 +23,7 @@ pub mod language; pub mod local_site; pub mod local_site_rate_limit; pub mod local_user; +pub mod local_user_vote_display_mode; pub mod login_token; pub mod moderator; pub mod password_reset_request; diff --git a/crates/db_views/src/comment_report_view.rs b/crates/db_views/src/comment_report_view.rs index 6f3322b39..817ae1694 100644 --- a/crates/db_views/src/comment_report_view.rs +++ b/crates/db_views/src/comment_report_view.rs @@ -223,6 +223,7 @@ mod tests { community::{Community, CommunityInsertForm, CommunityModerator, CommunityModeratorForm}, instance::Instance, local_user::{LocalUser, LocalUserInsertForm}, + local_user_vote_display_mode::LocalUserVoteDisplayMode, person::{Person, PersonInsertForm}, post::{Post, PostInsertForm}, }, @@ -258,6 +259,7 @@ mod tests { let timmy_local_user = LocalUser::create(pool, &new_local_user).await.unwrap(); let timmy_view = LocalUserView { local_user: timmy_local_user, + local_user_vote_display_mode: LocalUserVoteDisplayMode::default(), person: inserted_timmy.clone(), counts: Default::default(), }; diff --git a/crates/db_views/src/comment_view.rs b/crates/db_views/src/comment_view.rs index a72d0b210..40065672c 100644 --- a/crates/db_views/src/comment_view.rs +++ b/crates/db_views/src/comment_view.rs @@ -441,6 +441,7 @@ mod tests { instance::Instance, language::Language, local_user::{LocalUser, LocalUserInsertForm}, + local_user_vote_display_mode::LocalUserVoteDisplayMode, person::{Person, PersonInsertForm}, person_block::{PersonBlock, PersonBlockForm}, post::{Post, PostInsertForm}, @@ -614,6 +615,7 @@ mod tests { let timmy_local_user_view = LocalUserView { local_user: inserted_timmy_local_user.clone(), + local_user_vote_display_mode: LocalUserVoteDisplayMode::default(), person: inserted_timmy_person.clone(), counts: Default::default(), }; diff --git a/crates/db_views/src/local_user_view.rs b/crates/db_views/src/local_user_view.rs index cb9ab86c4..96d6a3530 100644 --- a/crates/db_views/src/local_user_view.rs +++ b/crates/db_views/src/local_user_view.rs @@ -4,7 +4,7 @@ use diesel::{result::Error, BoolExpressionMethods, ExpressionMethods, JoinOnDsl, use diesel_async::RunQueryDsl; use lemmy_db_schema::{ newtypes::{LocalUserId, PersonId}, - schema::{local_user, person, person_aggregates}, + schema::{local_user, local_user_vote_display_mode, person, person_aggregates}, utils::{ functions::{coalesce, lower}, DbConn, @@ -33,6 +33,7 @@ fn queries<'a>( ) -> Queries>, impl ListFn<'a, LocalUserView, ListMode>> { let selection = ( local_user::all_columns, + local_user_vote_display_mode::all_columns, person::all_columns, person_aggregates::all_columns, ); @@ -58,6 +59,7 @@ fn queries<'a>( _ => query, }; query + .inner_join(local_user_vote_display_mode::table) .inner_join(person_aggregates::table.on(person::id.eq(person_aggregates::person_id))) .select(selection) .first::(&mut conn) @@ -68,10 +70,11 @@ fn queries<'a>( match mode { ListMode::AdminsWithEmails => { local_user::table - .filter(local_user::email.is_not_null()) - .filter(local_user::admin.eq(true)) + .inner_join(local_user_vote_display_mode::table) .inner_join(person::table) .inner_join(person_aggregates::table.on(person::id.eq(person_aggregates::person_id))) + .filter(local_user::email.is_not_null()) + .filter(local_user::admin.eq(true)) .select(selection) .load::(&mut conn) .await diff --git a/crates/db_views/src/post_report_view.rs b/crates/db_views/src/post_report_view.rs index ddd12d226..ea59d6b64 100644 --- a/crates/db_views/src/post_report_view.rs +++ b/crates/db_views/src/post_report_view.rs @@ -206,6 +206,7 @@ mod tests { community::{Community, CommunityInsertForm, CommunityModerator, CommunityModeratorForm}, instance::Instance, local_user::{LocalUser, LocalUserInsertForm}, + local_user_vote_display_mode::LocalUserVoteDisplayMode, person::{Person, PersonInsertForm}, post::{Post, PostInsertForm}, post_report::{PostReport, PostReportForm}, @@ -241,6 +242,7 @@ mod tests { let timmy_local_user = LocalUser::create(pool, &new_local_user).await.unwrap(); let timmy_view = LocalUserView { local_user: timmy_local_user, + local_user_vote_display_mode: LocalUserVoteDisplayMode::default(), person: inserted_timmy.clone(), counts: Default::default(), }; diff --git a/crates/db_views/src/post_view.rs b/crates/db_views/src/post_view.rs index ed9cd0952..b9522d47b 100644 --- a/crates/db_views/src/post_view.rs +++ b/crates/db_views/src/post_view.rs @@ -749,6 +749,7 @@ mod tests { instance_block::{InstanceBlock, InstanceBlockForm}, language::Language, local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm}, + local_user_vote_display_mode::LocalUserVoteDisplayMode, person::{Person, PersonInsertForm}, person_block::{PersonBlock, PersonBlockForm}, post::{Post, PostHide, PostInsertForm, PostLike, PostLikeForm, PostRead, PostUpdateForm}, @@ -871,11 +872,13 @@ mod tests { let inserted_bot_post = Post::create(pool, &new_bot_post).await?; let local_user_view = LocalUserView { local_user: inserted_local_user, + local_user_vote_display_mode: LocalUserVoteDisplayMode::default(), person: inserted_person, counts: Default::default(), }; let blocked_local_user_view = LocalUserView { local_user: inserted_blocked_local_user, + local_user_vote_display_mode: LocalUserVoteDisplayMode::default(), person: inserted_blocked_person, counts: Default::default(), }; diff --git a/crates/db_views/src/structs.rs b/crates/db_views/src/structs.rs index 9b2d8d602..1a90f7c24 100644 --- a/crates/db_views/src/structs.rs +++ b/crates/db_views/src/structs.rs @@ -11,6 +11,7 @@ use lemmy_db_schema::{ local_site::LocalSite, local_site_rate_limit::LocalSiteRateLimit, local_user::LocalUser, + local_user_vote_display_mode::LocalUserVoteDisplayMode, person::Person, post::Post, post_report::PostReport, @@ -73,6 +74,7 @@ pub struct CommentView { /// A local user view. pub struct LocalUserView { pub local_user: LocalUser, + pub local_user_vote_display_mode: LocalUserVoteDisplayMode, pub person: Person, pub counts: PersonAggregates, } diff --git a/migrations/2024-02-12-211114_add_vote_display_mode_setting/down.sql b/migrations/2024-02-12-211114_add_vote_display_mode_setting/down.sql new file mode 100644 index 000000000..e6daaedde --- /dev/null +++ b/migrations/2024-02-12-211114_add_vote_display_mode_setting/down.sql @@ -0,0 +1,2 @@ +DROP TABLE local_user_vote_display_mode; + diff --git a/migrations/2024-02-12-211114_add_vote_display_mode_setting/up.sql b/migrations/2024-02-12-211114_add_vote_display_mode_setting/up.sql new file mode 100644 index 000000000..a76e3c122 --- /dev/null +++ b/migrations/2024-02-12-211114_add_vote_display_mode_setting/up.sql @@ -0,0 +1,18 @@ +-- Create an extra table to hold local user vote display settings +-- Score and Upvote percentage are turned on by default. +CREATE TABLE local_user_vote_display_mode ( + local_user_id int REFERENCES local_user ON UPDATE CASCADE ON DELETE CASCADE NOT NULL, + score boolean DEFAULT TRUE NOT NULL, + upvotes boolean DEFAULT FALSE NOT NULL, + downvotes boolean DEFAULT FALSE NOT NULL, + upvote_percentage boolean DEFAULT TRUE NOT NULL, + PRIMARY KEY (local_user_id) +); + +-- Insert rows for every local user +INSERT INTO local_user_vote_display_mode (local_user_id) +SELECT + id +FROM + local_user; +