From c395a081066eeeb4f9ea3d78a9a32bf31ca2c42b Mon Sep 17 00:00:00 2001 From: Luca Pagliaro Date: Tue, 14 Jan 2025 16:53:02 +0100 Subject: [PATCH 1/6] feat: add feedToFilters logic for blocked users --- __tests__/__snapshots__/feeds.ts.snap | 3 +++ __tests__/feeds.ts | 30 +++++++++++++++++++++++++++ src/common/feedGenerator.ts | 22 +++++++++++--------- src/integrations/feed/configs.ts | 4 +++- 4 files changed, 48 insertions(+), 11 deletions(-) diff --git a/__tests__/__snapshots__/feeds.ts.snap b/__tests__/__snapshots__/feeds.ts.snap index a30611054..b469dd621 100644 --- a/__tests__/__snapshots__/feeds.ts.snap +++ b/__tests__/__snapshots__/feeds.ts.snap @@ -10,6 +10,7 @@ Object { "excludeSourceTypes": Array [], "excludeSources": Array [], "excludeTypes": Array [], + "excludeUsers": Array [], "followingSources": Array [], "followingUsers": Array [], "includeTags": Array [], @@ -27,6 +28,7 @@ Object { ], "excludeSources": Array [], "excludeTypes": Array [], + "excludeUsers": Array [], "followingSources": Array [], "followingUsers": Array [], "includeTags": Array [], @@ -42,6 +44,7 @@ Object { "excludeSourceTypes": Array [], "excludeSources": Array [], "excludeTypes": Array [], + "excludeUsers": Array [], "followingSources": Array [], "followingUsers": Array [], "includeTags": Array [], diff --git a/__tests__/feeds.ts b/__tests__/feeds.ts index 1827e7351..15d4e244a 100644 --- a/__tests__/feeds.ts +++ b/__tests__/feeds.ts @@ -394,6 +394,7 @@ describe('query anonymousFeed', () => { const filters = await feedToFilters(con, '1', '1'); delete filters.sourceIds; delete filters.excludeTypes; + delete filters.excludeUsers; const res = await client.query(QUERY, { variables: { ...variables, filters }, }); @@ -2960,6 +2961,35 @@ describe('function feedToFilters', () => { expect(filters.followingUsers).toEqual(['2', '3']); }); + it('should return filters having excluded users based on content preference', async () => { + loggedUser = '1'; + await saveAdvancedSettingsFiltersFixtures(); + await con.getRepository(ContentPreferenceUser).save([ + { + feedId: '1', + userId: '1', + status: ContentPreferenceStatus.Follow, + referenceId: '2', + }, + { + feedId: '1', + userId: '1', + status: ContentPreferenceStatus.Subscribed, + referenceId: '3', + }, + { + feedId: '1', + userId: '1', + status: ContentPreferenceStatus.Blocked, + referenceId: '4', + type: ContentPreferenceType.User, + }, + ]); + const filters = await feedToFilters(con, '1', '1'); + expect(filters.followingUsers).toEqual(['2', '3']); + expect(filters.excludeUsers).toEqual(['4']); + }); + it('should return filters having excluded content types based on advanced settings', async () => { loggedUser = '1'; await saveAdvancedSettingsFiltersFixtures(); diff --git a/src/common/feedGenerator.ts b/src/common/feedGenerator.ts index 621e11a62..3a4871cd4 100644 --- a/src/common/feedGenerator.ts +++ b/src/common/feedGenerator.ts @@ -101,7 +101,7 @@ type RawFiltersData = { | null; tags: Pick[] | null; words: Pick[] | null; - users: Pick[] | null; + users: Pick[] | null; sources: Pick[] | null; memberships: { sourceId: SourceMember['sourceId']; hide: boolean }[] | null; feeds: Pick[] | null; @@ -136,12 +136,11 @@ const getRawFiltersData = async ( ), rawFilterSelect(con, 'users', (qb) => qb - .select('"referenceId"') + .select(['"referenceId"', 'status']) .from(ContentPreference, 'u') .where('"feedId" = $1') .andWhere('"userId" = $2') - .andWhere(`type = '${ContentPreferenceType.User}'`) - .andWhere(`status != '${ContentPreferenceStatus.Blocked}'`), + .andWhere(`type = '${ContentPreferenceType.User}'`), ), rawFilterSelect(con, 'sources', (qb) => qb @@ -253,15 +252,17 @@ const wordsToFilters = ({ const usersToFilters = ({ users, -}: RawFiltersData): { - followingUsers: string[]; -} => { +}: RawFiltersData): Record<'followingUsers' | 'excludeUsers', string[]> => { return (users || []).reduce>( - (acc, value) => { - acc.followingUsers.push(value.referenceId); + (acc, { referenceId, status }) => { + if (status === ContentPreferenceStatus.Blocked) { + acc.excludeUsers.push(referenceId); + } else { + acc.followingUsers.push(referenceId); + } return acc; }, - { followingUsers: [] }, + { followingUsers: [], excludeUsers: [] }, ); }; @@ -663,6 +664,7 @@ export interface AnonymousFeedFilters { blockedWords?: string[]; excludeSourceTypes?: string[]; followingUsers?: string[]; + excludeUsers?: string[]; followingSources?: string[]; flags?: FeedFlagsFilters; } diff --git a/src/integrations/feed/configs.ts b/src/integrations/feed/configs.ts index e4050e0c2..a7b8b4ed6 100644 --- a/src/integrations/feed/configs.ts +++ b/src/integrations/feed/configs.ts @@ -162,7 +162,9 @@ const addFiltersToConfig = ({ } if (filters.followingUsers?.length && opts.includeAllowedUsers) { baseConfig.allowed_author_ids = mergeSingleFilter( - baseConfig.allowed_author_ids, + baseConfig.allowed_author_ids?.filter( + (id) => !filters.excludeUsers?.includes(id), + ), filters.followingUsers, ); } From 60a7512fcf40a7eda0d50384b415dad4275365d0 Mon Sep 17 00:00:00 2001 From: Luca Pagliaro Date: Tue, 14 Jan 2025 17:39:19 +0100 Subject: [PATCH 2/6] fix: using blocked_author_ids as feed filter --- src/integrations/feed/configs.ts | 12 +++++++++--- src/integrations/feed/types.ts | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/integrations/feed/configs.ts b/src/integrations/feed/configs.ts index a7b8b4ed6..ff00ebce9 100644 --- a/src/integrations/feed/configs.ts +++ b/src/integrations/feed/configs.ts @@ -17,6 +17,7 @@ export type Options = { includeBlockedTags?: boolean; includeAllowedSources?: boolean; includeBlockedSources?: boolean; + includeBlockedUsers?: boolean; includeAllowedUsers?: boolean; includeSourceMemberships?: boolean; includePostTypes?: boolean; @@ -162,13 +163,18 @@ const addFiltersToConfig = ({ } if (filters.followingUsers?.length && opts.includeAllowedUsers) { baseConfig.allowed_author_ids = mergeSingleFilter( - baseConfig.allowed_author_ids?.filter( - (id) => !filters.excludeUsers?.includes(id), - ), + baseConfig.allowed_author_ids, filters.followingUsers, ); } + if (filters.excludeUsers?.length && opts.includeBlockedUsers) { + baseConfig.blocked_author_ids = mergeSingleFilter( + baseConfig.blocked_author_ids, + filters.excludeUsers, + ); + } + return baseConfig; }; diff --git a/src/integrations/feed/types.ts b/src/integrations/feed/types.ts index af2d63610..3e196ab2c 100644 --- a/src/integrations/feed/types.ts +++ b/src/integrations/feed/types.ts @@ -55,6 +55,7 @@ export type FeedConfig = { allowed_content_curations?: string[]; blocked_title_words?: string[]; allowed_author_ids?: string[]; + blocked_author_ids?: string[]; followed_user_ids?: string[]; followed_sources?: string[]; squad_ids?: string[]; From 41dc2db8f6986f6ef7fa3c039c52178d37c19b14 Mon Sep 17 00:00:00 2001 From: Luca Pagliaro Date: Wed, 15 Jan 2025 14:30:04 +0100 Subject: [PATCH 3/6] feat: add option for enabling filtering users in feed/customFeed/popular --- src/integrations/feed/generators.ts | 1 + src/schema/feeds.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/integrations/feed/generators.ts b/src/integrations/feed/generators.ts index 2f9945e97..ede1df5b6 100644 --- a/src/integrations/feed/generators.ts +++ b/src/integrations/feed/generators.ts @@ -113,6 +113,7 @@ export const feedGenerators: Partial> = includeBlockedTags: true, includeContentCuration: true, includeBlockedWords: true, + includeBlockedUsers: true, }, ), 'popular', diff --git a/src/schema/feeds.ts b/src/schema/feeds.ts index 040856931..7935ba5b7 100644 --- a/src/schema/feeds.ts +++ b/src/schema/feeds.ts @@ -1513,6 +1513,7 @@ export const resolvers: IResolvers = traceResolvers< includeContentCuration: true, includeBlockedWords: true, includeAllowedUsers: true, + includeBlockedUsers: true, includeAllowedSources: true, feedId: feedId, }, From c6e5f0977ce3dac013772cffaa253012a4ad9088 Mon Sep 17 00:00:00 2001 From: Luca Pagliaro Date: Wed, 15 Jan 2025 16:07:31 +0100 Subject: [PATCH 4/6] feat: add missing opts setting --- src/integrations/feed/generators.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/integrations/feed/generators.ts b/src/integrations/feed/generators.ts index ede1df5b6..a264c818f 100644 --- a/src/integrations/feed/generators.ts +++ b/src/integrations/feed/generators.ts @@ -88,6 +88,7 @@ const opts: Options = { includeBlockedWords: true, includeFollowedSources: true, includeFollowedUsers: true, + includeBlockedUsers: true, }; export const feedGenerators: Partial> = From 90989b13dba7e9c570569afa6d3c81cb0509f071 Mon Sep 17 00:00:00 2001 From: Luca Pagliaro Date: Wed, 15 Jan 2025 16:46:02 +0100 Subject: [PATCH 5/6] test: integration feed with blocked users --- __tests__/integrations/feed.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/__tests__/integrations/feed.ts b/__tests__/integrations/feed.ts index 71b811342..355974432 100644 --- a/__tests__/integrations/feed.ts +++ b/__tests__/integrations/feed.ts @@ -313,6 +313,7 @@ describe('FeedPreferencesConfigGenerator', () => { includeBlockedWords: true, includeFollowedUsers: true, includeFollowedSources: true, + includeBlockedUsers: true, }, ); @@ -327,6 +328,7 @@ describe('FeedPreferencesConfigGenerator', () => { blocked_sources: expect.arrayContaining(['a', 'b']), blocked_tags: expect.arrayContaining(['python', 'java']), blocked_title_words: expect.arrayContaining(['word-abc', 'word-def']), + blocked_author_ids: expect.arrayContaining(['4']), followed_sources: expect.arrayContaining(['c', 'p']), followed_user_ids: expect.arrayContaining(['2', '3']), allowed_post_types: postTypes.filter( From 3720fe3d132b0958c85bc1b23e06f6b46fe5e9cf Mon Sep 17 00:00:00 2001 From: capJavert Date: Wed, 15 Jan 2025 17:00:37 +0100 Subject: [PATCH 6/6] feat: digest support --- .../workers/__snapshots__/personalizedDigestEmail.ts.snap | 4 ++++ src/common/personalizedDigest.ts | 1 + 2 files changed, 5 insertions(+) diff --git a/__tests__/workers/__snapshots__/personalizedDigestEmail.ts.snap b/__tests__/workers/__snapshots__/personalizedDigestEmail.ts.snap index 3690d2897..e1e6ce3b7 100644 --- a/__tests__/workers/__snapshots__/personalizedDigestEmail.ts.snap +++ b/__tests__/workers/__snapshots__/personalizedDigestEmail.ts.snap @@ -207,6 +207,7 @@ Object { exports[`personalizedDigestEmail worker should generate personalized digest for user in timezone ahead UTC 2`] = ` Object { "allowed_tags": Array [], + "blocked_author_ids": Array [], "blocked_sources": Array [], "blocked_tags": Array [], "date_from": Any, @@ -328,6 +329,7 @@ Object { exports[`personalizedDigestEmail worker should generate personalized digest for user in timezone behind UTC 2`] = ` Object { "allowed_tags": Array [], + "blocked_author_ids": Array [], "blocked_sources": Array [], "blocked_tags": Array [], "date_from": Any, @@ -449,6 +451,7 @@ Object { exports[`personalizedDigestEmail worker should generate personalized digest for user with provided config 2`] = ` Object { "allowed_tags": Array [], + "blocked_author_ids": Array [], "blocked_sources": Array [], "blocked_tags": Array [], "date_from": Any, @@ -570,6 +573,7 @@ Object { exports[`personalizedDigestEmail worker should generate personalized digest for user with subscription 2`] = ` Object { "allowed_tags": Array [], + "blocked_author_ids": Array [], "blocked_sources": Array [], "blocked_tags": Array [], "date_from": Any, diff --git a/src/common/personalizedDigest.ts b/src/common/personalizedDigest.ts index 44a6171ef..514214d1e 100644 --- a/src/common/personalizedDigest.ts +++ b/src/common/personalizedDigest.ts @@ -255,6 +255,7 @@ export const getPersonalizedDigestEmailPayload = async ({ ) || [], page_size: feature.maxPosts, total_pages: 1, + blocked_author_ids: feedConfig.excludeUsers, }; const feedResponse = await personalizedDigestFeedClient.fetchFeed( { log: logger },