Repository: LemmyNet/lemmy (language: rust)

Issue:
Blocking an instance should not stop me from seeing posts of a community I've subscribed to from that instance
### Requirements

- [X] Is this a feature request? For questions or discussions use https://lemmy.ml/c/lemmy_support
- [X] Did you check to see if this issue already exists?
- [X] Is this only a feature request? Do not put multiple feature requests in one issue.
- [X] Is this a backend issue? Use the [lemmy-ui](https://github.com/LemmyNet/lemmy-ui) repo for UI / frontend issues.

### Is your proposal related to a problem?

When I block an instance I can't see any posts from communities hosted on that instance even if I subscribed to some of the communities from that instance.

### Describe the solution you'd like.

I would love to still be able to see the posts from my subscribed communities even if these communities are hosted on an instance that I blocked.

### Describe alternatives you've considered.

I don't think there is any alternative solution here.

### Additional context

_No response_


Discussion / hints:
I'm pretty sure this is by design.

Not sure I see the value in pulling content down from a blocked instance -- why even block it in the first place if you still wanted that content?
It would make filtering the all feed so much more powerful. For example I enjoy seeing the content of some select nsfw communities in my subscribed feed but I don't want to see any other nsfw communities if I happen to browse the all feed to discover new communities or if I exhausted my subscribed feed. Right now I can't block lemmynsfw for example because that would block my subscribed communities as well.
This makes sense, anyway posts from blocked instances are still available in the database. But it needs someone to implement the necessary changes to db queries.
I'm not sure about this one, logically if you've blocked an instance, that should take precedence / override any follows. 

@Tywele what about just blocking the communities you don't want to see, since that's actually what you're trying to acheive? (IE you don't actually want to block that instance, just certain communities).
What's the logic behind that? A follow/subscription is more specific so it should definitely be a higher priority than a generic block.

Because there are so many more communities that I don't want to see from some instances than I want to see. It would just be easier if a subscription of a community would override the block of an instance for that specific community.

Fix patch:
diff --git a/crates/db_views/src/comment/comment_view.rs b/crates/db_views/src/comment/comment_view.rs
index d9f3ab2dfc..a5962c040d 100644
--- a/crates/db_views/src/comment/comment_view.rs
+++ b/crates/db_views/src/comment/comment_view.rs
@@ -1,4 +1,7 @@
-use crate::structs::{CommentSlimView, CommentView};
+use crate::{
+  structs::{CommentSlimView, CommentView},
+  utils::filter_blocked,
+};
 use diesel::{
   dsl::exists,
   result::Error,
@@ -233,11 +236,7 @@ impl CommentQuery<'_> {
         ),
       ));
 
-      // Don't show blocked communities or persons
-      query = query
-        .filter(instance_actions::blocked.is_null())
-        .filter(community_actions::blocked.is_null())
-        .filter(person_actions::blocked.is_null());
+      query = query.filter(filter_blocked());
     };
 
     if !o.local_user.show_nsfw(site) {
diff --git a/crates/db_views/src/lib.rs b/crates/db_views/src/lib.rs
index 594303fda9..342ca94630 100644
--- a/crates/db_views/src/lib.rs
+++ b/crates/db_views/src/lib.rs
@@ -22,3 +22,5 @@ pub mod reports;
 #[cfg(feature = "full")]
 pub mod site;
 pub mod structs;
+#[cfg(feature = "full")]
+pub mod utils;
diff --git a/crates/db_views/src/post/post_view.rs b/crates/db_views/src/post/post_view.rs
index 8a5e07fc4d..68c838f8af 100644
--- a/crates/db_views/src/post/post_view.rs
+++ b/crates/db_views/src/post/post_view.rs
@@ -1,4 +1,7 @@
-use crate::structs::{PaginationCursor, PostView};
+use crate::{
+  structs::{PaginationCursor, PostView},
+  utils::filter_blocked,
+};
 use diesel::{
   debug_query,
   dsl::{exists, not},
@@ -571,10 +574,7 @@ impl<'a> PostQuery<'a> {
         ));
       }
 
-      // Don't show blocked instances, communities or persons
-      query = query.filter(community_actions::blocked.is_null());
-      query = query.filter(instance_actions::blocked.is_null());
-      query = query.filter(person_actions::blocked.is_null());
+      query = query.filter(filter_blocked());
     }
 
     let (limit, offset) = limit_and_offset(o.page, o.limit)?;
@@ -1606,6 +1606,12 @@ mod tests {
   #[serial]
   async fn post_listing_instance_block(data: &mut Data) -> LemmyResult<()> {
     const POST_FROM_BLOCKED_INSTANCE: &str = "post on blocked instance";
+    const POST_LISTING_WITH_BLOCKED: [&str; 4] = [
+      POST_FROM_BLOCKED_INSTANCE,
+      POST_WITH_TAGS,
+      POST_BY_BOT,
+      POST,
+    ];
 
     let pool = &data.pool();
     let pool = &mut pool.into();
@@ -1632,15 +1638,7 @@ mod tests {
 
     // no instance block, should return all posts
     let post_listings_all = data.default_post_query().list(&data.site, pool).await?;
-    assert_eq!(
-      vec![
-        POST_FROM_BLOCKED_INSTANCE,
-        POST_WITH_TAGS,
-        POST_BY_BOT,
-        POST
-      ],
-      names(&post_listings_all)
-    );
+    assert_eq!(POST_LISTING_WITH_BLOCKED, *names(&post_listings_all));
 
     // block the instance
     let block_form = InstanceBlockForm {
@@ -1659,18 +1657,19 @@ mod tests {
       .iter()
       .all(|p| p.post.id != post_from_blocked_instance.id));
 
+    // Follow community from the blocked instance to see posts anyway
+    let mut follow_form =
+      CommunityFollowerForm::new(inserted_community.id, data.tegan_local_user_view.person.id);
+    follow_form.state = Some(CommunityFollowerState::Accepted);
+    CommunityFollower::follow(pool, &follow_form).await?;
+    let post_listings_bypass = data.default_post_query().list(&data.site, pool).await?;
+    assert_eq!(POST_LISTING_WITH_BLOCKED, *names(&post_listings_bypass));
+    CommunityFollower::unfollow(pool, &follow_form).await?;
+
     // after unblocking it should return all posts again
     InstanceBlock::unblock(pool, &block_form).await?;
     let post_listings_blocked = data.default_post_query().list(&data.site, pool).await?;
-    assert_eq!(
-      vec![
-        POST_FROM_BLOCKED_INSTANCE,
-        POST_WITH_TAGS,
-        POST_BY_BOT,
-        POST
-      ],
-      names(&post_listings_blocked)
-    );
+    assert_eq!(POST_LISTING_WITH_BLOCKED, *names(&post_listings_blocked));
 
     Instance::delete(pool, blocked_instance.id).await?;
     Ok(())
diff --git a/crates/db_views/src/utils.rs b/crates/db_views/src/utils.rs
new file mode 100644
index 0000000000..71f4e2d36c
--- /dev/null
+++ b/crates/db_views/src/utils.rs
@@ -0,0 +1,13 @@
+use diesel::{BoolExpressionMethods, ExpressionMethods};
+use lemmy_db_schema::schema::{community_actions, instance_actions, person_actions};
+
+/// Hide all content from blocked communities and persons. Content from blocked instances is also
+/// hidden, unless the user followed the community explicitly.
+#[diesel::dsl::auto_type]
+pub(crate) fn filter_blocked() -> _ {
+  instance_actions::blocked
+    .is_null()
+    .or(community_actions::followed.is_not_null())
+    .and(community_actions::blocked.is_null())
+    .and(person_actions::blocked.is_null())
+}


Test patch: