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>
This commit is contained in:
Dessalines 2024-03-13 12:10:58 -04:00 committed by GitHub
parent 45c56df4e8
commit 15f02f00a9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 211 additions and 5 deletions

View file

@ -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()))
}

View file

@ -86,8 +86,6 @@ pub struct SaveUserSettings {
pub show_nsfw: Option<bool>,
pub blur_nsfw: Option<bool>,
pub auto_expand: Option<bool>,
/// Show post and comment scores.
pub show_scores: Option<bool>,
/// Your user's theme.
pub theme: Option<String>,
pub default_sort_type: Option<SortType>,
@ -122,6 +120,7 @@ pub struct SaveUserSettings {
pub open_links_in_new_tab: Option<bool>,
/// Enable infinite scroll
pub infinite_scroll_enabled: Option<bool>,
/// A post-view mode that changes how multiple post listings look.
pub post_listing_mode: Option<PostListingMode>,
/// Whether to allow keyboard navigation (for browsing and interacting with posts and comments).
pub enable_keyboard_navigation: Option<bool>,
@ -129,6 +128,11 @@ pub struct SaveUserSettings {
pub enable_animated_images: Option<bool>,
/// Whether to auto-collapse bot comments.
pub collapse_bot_comments: Option<bool>,
/// Some vote display mode settings
pub show_scores: Option<bool>,
pub show_upvotes: Option<bool>,
pub show_downvotes: Option<bool>,
pub show_upvote_percentage: Option<bool>,
}
#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)]

View file

@ -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(),
};

View file

@ -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<LocalUser>,
pub vote_display_mode_settings: Option<LocalUserVoteDisplayMode>,
#[serde(default)]
pub followed_communities: Vec<ObjectId<ApubCommunity>>,
#[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()

View file

@ -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(

View file

@ -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<Self, Error> {
let conn = &mut get_conn(pool).await?;
local_user_vote_display_mode::table
.first::<Self>(conn)
.await
}
pub async fn create(
pool: &mut DbPool<'_>,
form: &LocalUserVoteDisplayModeInsertForm,
) -> Result<Self, Error> {
let conn = &mut get_conn(pool).await?;
insert_into(local_user_vote_display_mode::table)
.values(form)
.get_result::<Self>(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::<Self>(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()
}
}

View file

@ -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;

View file

@ -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,

View file

@ -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).

View file

@ -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<bool>,
pub upvotes: Option<bool>,
pub downvotes: Option<bool>,
pub upvote_percentage: Option<bool>,
}
#[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<bool>,
pub upvotes: Option<bool>,
pub downvotes: Option<bool>,
pub upvote_percentage: Option<bool>,
}

View file

@ -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;

View file

@ -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(),
};

View file

@ -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(),
};

View file

@ -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 ReadFn<'a, LocalUserView, ReadBy<'a>>, 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::<LocalUserView>(&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::<LocalUserView>(&mut conn)
.await

View file

@ -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(),
};

View file

@ -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(),
};

View file

@ -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,
}

View file

@ -0,0 +1,2 @@
DROP TABLE local_user_vote_display_mode;

View file

@ -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;