diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 35ee941..d9d953e 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -259,6 +259,10 @@ paths: description: Receive this actor's reposts in home timeline? type: boolean default: true + replies: + description: Receive this actor's replies in home timeline? + type: boolean + default: true responses: 200: description: Successfully followed, or actor was already followed @@ -732,7 +736,11 @@ components: type: boolean default: false showing_reblogs: - description: Are you receiving this user's boosts in your home timeline? + description: Are you receiving this user's reposts in your home timeline? + type: boolean + default: true + showing_replies: + description: Are you receiving this user's replies in your home timeline? type: boolean default: true Signature: diff --git a/src/mastodon_api/accounts/helpers.rs b/src/mastodon_api/accounts/helpers.rs index 9eda178..2234d1f 100644 --- a/src/mastodon_api/accounts/helpers.rs +++ b/src/mastodon_api/accounts/helpers.rs @@ -40,6 +40,11 @@ pub async fn get_relationship( relationship_map.showing_reblogs = false; }; }, + RelationshipType::HideReplies => { + if relationship.is_direct(source_id, target_id)? { + relationship_map.showing_replies = false; + }; + }, }; }; Ok(relationship_map) @@ -93,6 +98,7 @@ mod tests { assert_eq!(relationship.subscription_to, false); assert_eq!(relationship.subscription_from, false); assert_eq!(relationship.showing_reblogs, true); + assert_eq!(relationship.showing_replies, true); // Follow request let follow_request = create_follow_request(db_client, &user_1.id, &user_2.id).await.unwrap(); let relationship = get_relationship(db_client, &user_1.id, &user_2.id).await.unwrap(); diff --git a/src/mastodon_api/accounts/types.rs b/src/mastodon_api/accounts/types.rs index 21f77ec..f07139e 100644 --- a/src/mastodon_api/accounts/types.rs +++ b/src/mastodon_api/accounts/types.rs @@ -202,10 +202,13 @@ pub struct RelationshipMap { pub subscription_to: bool, pub subscription_from: bool, pub showing_reblogs: bool, + pub showing_replies: bool, } fn default_showing_reblogs() -> bool { true } +fn default_showing_replies() -> bool { true } + impl Default for RelationshipMap { fn default() -> Self { Self { @@ -216,6 +219,7 @@ impl Default for RelationshipMap { subscription_to: false, subscription_from: false, showing_reblogs: default_showing_reblogs(), + showing_replies: default_showing_replies(), } } } @@ -224,6 +228,8 @@ impl Default for RelationshipMap { pub struct FollowData { #[serde(default = "default_showing_reblogs")] pub reblogs: bool, + #[serde(default = "default_showing_replies")] + pub replies: bool, } fn default_page_size() -> i64 { 20 } diff --git a/src/mastodon_api/accounts/views.rs b/src/mastodon_api/accounts/views.rs index 44858f5..5bf1d9b 100644 --- a/src/mastodon_api/accounts/views.rs +++ b/src/mastodon_api/accounts/views.rs @@ -33,7 +33,9 @@ use crate::models::relationships::queries::{ get_follow_request_by_path, get_followers, get_following, + hide_replies, hide_reposts, + show_replies, show_reposts, unfollow, }; @@ -284,6 +286,11 @@ async fn follow_account( } else { hide_reposts(db_client, ¤t_user.id, &target.id).await?; }; + if data.replies { + show_replies(db_client, ¤t_user.id, &target.id).await?; + } else { + hide_replies(db_client, ¤t_user.id, &target.id).await?; + }; let relationship = get_relationship( db_client, ¤t_user.id, diff --git a/src/models/posts/queries.rs b/src/models/posts/queries.rs index 30be705..b9b81f2 100644 --- a/src/models/posts/queries.rs +++ b/src/models/posts/queries.rs @@ -284,6 +284,21 @@ pub async fn get_home_timeline( AND repost_of.author_id = $current_user_id ) ) + AND ( + post.in_reply_to_id IS NULL + OR NOT EXISTS ( + SELECT 1 FROM relationship + WHERE + source_id = $current_user_id + AND target_id = post.author_id + AND relationship_type = {relationship_hide_replies} + ) + OR EXISTS ( + SELECT 1 FROM post AS in_reply_to + WHERE in_reply_to.id = post.in_reply_to_id + AND in_reply_to.author_id = $current_user_id + ) + ) ) OR EXISTS ( SELECT 1 FROM mention @@ -301,6 +316,7 @@ pub async fn get_home_timeline( relationship_follow=i16::from(&RelationshipType::Follow), relationship_subscription=i16::from(&RelationshipType::Subscription), relationship_hide_reposts=i16::from(&RelationshipType::HideReposts), + relationship_hide_replies=i16::from(&RelationshipType::HideReplies), visibility_filter=build_visibility_filter(), ); let query = query!( diff --git a/src/models/relationships/queries.rs b/src/models/relationships/queries.rs index f07cd44..256cbad 100644 --- a/src/models/relationships/queries.rs +++ b/src/models/relationships/queries.rs @@ -411,3 +411,37 @@ pub async fn show_reposts( ).await?; Ok(()) } + +pub async fn hide_replies( + db_client: &impl GenericClient, + source_id: &Uuid, + target_id: &Uuid, +) -> Result<(), DatabaseError> { + db_client.execute( + " + INSERT INTO relationship (source_id, target_id, relationship_type) + VALUES ($1, $2, $3) + ON CONFLICT (source_id, target_id, relationship_type) DO NOTHING + ", + &[&source_id, &target_id, &RelationshipType::HideReplies], + ).await?; + Ok(()) +} + +pub async fn show_replies( + db_client: &impl GenericClient, + source_id: &Uuid, + target_id: &Uuid, +) -> Result<(), DatabaseError> { + // Does not return NotFound error + db_client.execute( + " + DELETE FROM relationship + WHERE + source_id = $1 AND target_id = $2 + AND relationship_type = $3 + ", + &[&source_id, &target_id, &RelationshipType::HideReplies], + ).await?; + Ok(()) +} diff --git a/src/models/relationships/types.rs b/src/models/relationships/types.rs index 6a4aa30..7959173 100644 --- a/src/models/relationships/types.rs +++ b/src/models/relationships/types.rs @@ -13,6 +13,7 @@ pub enum RelationshipType { FollowRequest, Subscription, HideReposts, + HideReplies, } impl From<&RelationshipType> for i16 { @@ -22,6 +23,7 @@ impl From<&RelationshipType> for i16 { RelationshipType::FollowRequest => 2, RelationshipType::Subscription => 3, RelationshipType::HideReposts => 4, + RelationshipType::HideReplies => 5, } } } @@ -35,6 +37,7 @@ impl TryFrom for RelationshipType { 2 => Self::FollowRequest, 3 => Self::Subscription, 4 => Self::HideReposts, + 5 => Self::HideReplies, _ => return Err(ConversionError), }; Ok(relationship_type)