diff --git a/backend/src/graphql/resolvers/helpers/filterForMutedUsers.ts b/backend/src/graphql/resolvers/helpers/filterForMutedUsers.ts index 967ed2265..a298d1881 100644 --- a/backend/src/graphql/resolvers/helpers/filterForMutedUsers.ts +++ b/backend/src/graphql/resolvers/helpers/filterForMutedUsers.ts @@ -8,6 +8,8 @@ import { getMutedUsers } from '@graphql/resolvers/users' export const filterForMutedUsers = async (params, context) => { if (!context.user) return params + // Skip mute filter for single post lookups (direct navigation by id or slug) + if (params.id || params.slug) return params const [mutedUsers] = await Promise.all([getMutedUsers(context)]) const mutedUsersIds = [...mutedUsers.map((user) => user.id)] if (!mutedUsersIds.length) return params diff --git a/backend/src/graphql/resolvers/users/blockedUsers.spec.ts b/backend/src/graphql/resolvers/users/blockedUsers.spec.ts index 07f1615de..83c4c4e8c 100644 --- a/backend/src/graphql/resolvers/users/blockedUsers.spec.ts +++ b/backend/src/graphql/resolvers/users/blockedUsers.spec.ts @@ -245,6 +245,81 @@ describe('blockUser', () => { }) }) }) + + describe('if the current user blocks and mutes the other user', () => { + beforeEach(async () => { + await currentUser.relateTo(blockedUser, 'blocked') + await currentUser.relateTo(blockedUser, 'muted') + }) + + it('the muted+blocked user post is still accessible by direct id lookup', async () => { + await expect(query({ query: Post, variables: { id: 'p23' } })).resolves.toMatchObject( + { + data: { + Post: [ + expect.objectContaining({ + id: 'p23', + title: 'A post written by the blocked user', + }), + ], + }, + }, + ) + }) + + describe('and the blocked+muted user has a pinned post', () => { + beforeEach(async () => { + const pinnedPost = await database.neode.create('Post', { + id: 'p-pinned', + title: 'A pinned post by the blocked user', + content: 'pinned content', + pinned: true, + }) + await pinnedPost.relateTo(blockedUser, 'author') + }) + + it('the pinned post is still accessible by id', async () => { + await expect( + query({ query: Post, variables: { id: 'p-pinned' } }), + ).resolves.toMatchObject({ + data: { + Post: [ + expect.objectContaining({ + id: 'p-pinned', + title: 'A pinned post by the blocked user', + pinned: true, + }), + ], + }, + }) + }) + + it('the pinned post shows up in the post list', async () => { + await expect( + query({ query: Post, variables: { orderBy: 'createdAt_asc' } }), + ).resolves.toMatchObject({ + data: { + Post: expect.arrayContaining([ + expect.objectContaining({ + id: 'p-pinned', + pinned: true, + }), + ]), + }, + }) + }) + + it('the non-pinned post from the muted+blocked user is still hidden in the feed', async () => { + const result = await query({ + query: Post, + variables: { orderBy: 'createdAt_asc' }, + }) + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + const postIds = result.data?.Post.map((p) => p.id) + expect(postIds).not.toContain('p23') + }) + }) + }) }) describe('from the perspective of the blocked user', () => { diff --git a/backend/src/graphql/resolvers/users/mutedUsers.spec.ts b/backend/src/graphql/resolvers/users/mutedUsers.spec.ts index 3202a0274..d86c7e9e0 100644 --- a/backend/src/graphql/resolvers/users/mutedUsers.spec.ts +++ b/backend/src/graphql/resolvers/users/mutedUsers.spec.ts @@ -241,6 +241,79 @@ describe('muteUser', () => { }, }) }) + + it("the muted user's post is still accessible by direct id lookup", async () => { + const { query } = createTestClient(server) + await expect(query({ query: Post, variables: { id: 'p23' } })).resolves.toMatchObject( + { + data: { + Post: [ + expect.objectContaining({ + id: 'p23', + title: 'A post written by the muted user', + }), + ], + }, + }, + ) + }) + + describe('but the muted user has a pinned post', () => { + beforeEach(async () => { + const pinnedPost = await neode.create('Post', { + id: 'p-pinned', + title: 'A pinned post by the muted user', + content: 'pinned content', + pinned: true, + }) + await pinnedPost.relateTo(mutedUser, 'author') + }) + + it('the pinned post still shows up in the post list', async () => { + const { query } = createTestClient(server) + await expect( + query({ query: Post, variables: { orderBy: 'createdAt_asc' } }), + ).resolves.toMatchObject({ + data: { + Post: expect.arrayContaining([ + expect.objectContaining({ + id: 'p-pinned', + title: 'A pinned post by the muted user', + pinned: true, + }), + ]), + }, + }) + }) + + it('the pinned post is accessible by id', async () => { + const { query } = createTestClient(server) + await expect( + query({ query: Post, variables: { id: 'p-pinned' } }), + ).resolves.toMatchObject({ + data: { + Post: [ + expect.objectContaining({ + id: 'p-pinned', + title: 'A pinned post by the muted user', + pinned: true, + }), + ], + }, + }) + }) + + it('the non-pinned post from the muted user is still hidden in the feed', async () => { + const { query } = createTestClient(server) + const result = await query({ + query: Post, + variables: { orderBy: 'createdAt_asc' }, + }) + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + const postIds = result.data?.Post.map((p) => p.id) + expect(postIds).not.toContain('p23') + }) + }) }) })