From 2995dd1dddbb1925ed98534a7fd4e457b0746716 Mon Sep 17 00:00:00 2001 From: Mia Date: Mon, 13 Jul 2026 17:56:25 +0100 Subject: [PATCH] feat(parakeet): repost-only mutes (atproto#5118) --- crates/lexica/src/app_bsky/actor.rs | 2 ++ crates/parakeet-db/src/models.rs | 3 +++ crates/parakeet-db/src/schema.rs | 2 ++ crates/parakeet/src/db.rs | 4 ++++ crates/parakeet/src/hydration/profile.rs | 8 +++++++- crates/parakeet/src/sql/profile_state.sql | 4 ++++ crates/parakeet/src/xrpc/app_bsky/graph/mutes.rs | 9 ++++++++- migrations/2026-08-13-193507-0000_repost-mutes/down.sql | 1 + migrations/2026-08-13-193507-0000_repost-mutes/up.sql | 1 + 9 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 migrations/2026-08-13-193507-0000_repost-mutes/down.sql create mode 100644 migrations/2026-08-13-193507-0000_repost-mutes/up.sql diff --git a/crates/lexica/src/app_bsky/actor.rs b/crates/lexica/src/app_bsky/actor.rs index 4d7bddb..7343ad4 100644 --- a/crates/lexica/src/app_bsky/actor.rs +++ b/crates/lexica/src/app_bsky/actor.rs @@ -11,6 +11,8 @@ use std::str::FromStr; #[serde(rename_all = "camelCase")] pub struct ProfileViewerState { pub muted: bool, + pub muted_only_reposts: bool, + pub muted_only_quoteposts: bool, #[serde(skip_serializing_if = "Option::is_none")] pub muted_by_list: Option, pub blocked_by: bool, diff --git a/crates/parakeet-db/src/models.rs b/crates/parakeet-db/src/models.rs index ebae92a..49e620c 100644 --- a/crates/parakeet-db/src/models.rs +++ b/crates/parakeet-db/src/models.rs @@ -391,9 +391,12 @@ pub struct Status { #[derive(Debug, Insertable, AsChangeset)] #[diesel(table_name = crate::schema::mutes)] #[diesel(check_for_backend(diesel::pg::Pg))] +#[diesel(treat_none_as_null = true)] pub struct NewMute<'a> { pub did: &'a str, pub subject: &'a str, + pub only_reposts: Option, + pub only_quoteposts: Option, } #[derive(Debug, Insertable, AsChangeset)] diff --git a/crates/parakeet-db/src/schema.rs b/crates/parakeet-db/src/schema.rs index 9c85b16..4f38825 100644 --- a/crates/parakeet-db/src/schema.rs +++ b/crates/parakeet-db/src/schema.rs @@ -233,6 +233,8 @@ diesel::table! { did -> Text, subject -> Text, created_at -> Timestamptz, + only_reposts -> Nullable, + only_quoteposts -> Nullable, } } diff --git a/crates/parakeet/src/db.rs b/crates/parakeet/src/db.rs index ebbe457..bbfbeea 100644 --- a/crates/parakeet/src/db.rs +++ b/crates/parakeet/src/db.rs @@ -29,6 +29,10 @@ pub struct ProfileStateRet { #[diesel(sql_type = Nullable)] pub muting: Option, #[diesel(sql_type = Nullable)] + pub only_reposts: Option, + #[diesel(sql_type = Nullable)] + pub only_quoteposts: Option, + #[diesel(sql_type = Nullable)] pub blocked: Option, #[diesel(sql_type = Nullable)] pub blocking: Option, diff --git a/crates/parakeet/src/hydration/profile.rs b/crates/parakeet/src/hydration/profile.rs index 20f9008..e34a158 100644 --- a/crates/parakeet/src/hydration/profile.rs +++ b/crates/parakeet/src/hydration/profile.rs @@ -73,8 +73,14 @@ fn build_viewer( .zip(data.actsub_reply) .map(|(post, reply)| ActivitySubscription { post, reply }); + let muted_only_reposts = data.only_reposts.unwrap_or_default(); + let muted_only_quoteposts = data.only_quoteposts.unwrap_or_default(); + let muted = data.muting.unwrap_or_default() && !(muted_only_reposts || muted_only_quoteposts); + ProfileViewerState { - muted: data.muting.unwrap_or_default(), + muted, + muted_only_reposts, + muted_only_quoteposts, muted_by_list: list_mute, blocked_by: data.blocked.unwrap_or_default(), // TODO: this doesn't factor for blocklists atm blocking, diff --git a/crates/parakeet/src/sql/profile_state.sql b/crates/parakeet/src/sql/profile_state.sql index f68d3d1..6d85f50 100644 --- a/crates/parakeet/src/sql/profile_state.sql +++ b/crates/parakeet/src/sql/profile_state.sql @@ -2,6 +2,7 @@ with vlb as (select * from v_list_block_exp where did = $1 and subject = any($2) vlm as (select * from v_list_mutes_exp where did = $1 and subject = any($2)), ps as (select * from profile_states where did = $1 and subject = any($2)), naf as (select * from notif_activity_subs where did = $1 and subject = any($2)), +mute as (select * from mutes where did = $1 and subject = any($2)), vlb2 as ( select subject as did, did as subject, list_uri is not null as blocked from v_list_block_exp @@ -13,6 +14,8 @@ select distinct on (did, subject) did, subject, muting, + mute.only_reposts, + mute.only_quoteposts, ps.blocked or vlb2.blocked as blocked, blocking, following, @@ -25,4 +28,5 @@ from ps full join vlb using (did, subject) full join vlm using (did, subject) full join vlb2 using (did, subject) +full join mute using (did, subject) full join naf using (did, subject); diff --git a/crates/parakeet/src/xrpc/app_bsky/graph/mutes.rs b/crates/parakeet/src/xrpc/app_bsky/graph/mutes.rs index 0375218..df1ae03 100644 --- a/crates/parakeet/src/xrpc/app_bsky/graph/mutes.rs +++ b/crates/parakeet/src/xrpc/app_bsky/graph/mutes.rs @@ -58,8 +58,11 @@ pub async fn get_mutes( } #[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct MuteActorReq { pub actor: String, + pub only_reposts: Option, + pub only_quoteposts: Option, } pub async fn mute_actor( @@ -72,11 +75,15 @@ pub async fn mute_actor( let data = models::NewMute { did: &auth.0, subject: &form.actor, + only_reposts: form.only_reposts, + only_quoteposts: form.only_quoteposts, }; diesel::insert_into(schema::mutes::table) .values(&data) - .on_conflict_do_nothing() + .on_conflict((schema::mutes::did, schema::mutes::subject)) + .do_update() + .set(&data) .execute(&mut conn) .await?; diff --git a/migrations/2026-08-13-193507-0000_repost-mutes/down.sql b/migrations/2026-08-13-193507-0000_repost-mutes/down.sql new file mode 100644 index 0000000..868c45a --- /dev/null +++ b/migrations/2026-08-13-193507-0000_repost-mutes/down.sql @@ -0,0 +1 @@ +alter table mutes drop column only_reposts, drop column only_quoteposts; diff --git a/migrations/2026-08-13-193507-0000_repost-mutes/up.sql b/migrations/2026-08-13-193507-0000_repost-mutes/up.sql new file mode 100644 index 0000000..bc23ce2 --- /dev/null +++ b/migrations/2026-08-13-193507-0000_repost-mutes/up.sql @@ -0,0 +1 @@ +alter table mutes add column only_reposts bool, add column only_quoteposts bool; -- 2.51.2