From b652e52e6b39f8111e8071b6f820c4690270a7c0 Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Wed, 27 Mar 2024 05:52:50 -0400 Subject: [PATCH 1/4] feat(settings): ignore bot notifications --- src/__mocks__/mock-state.ts | 1 + src/__mocks__/mockedData.ts | 22 ++++++++++++ src/context/App.tsx | 1 + src/hooks/useNotifications.ts | 68 +++++++++++++++++++++-------------- src/routes/Settings.tsx | 6 ++++ src/types.ts | 1 + src/typesGithub.ts | 2 ++ src/utils/helpers.ts | 1 + src/utils/subject.ts | 11 ++++-- 9 files changed, 85 insertions(+), 28 deletions(-) diff --git a/src/__mocks__/mock-state.ts b/src/__mocks__/mock-state.ts index ac5072e48..8175a7f1d 100644 --- a/src/__mocks__/mock-state.ts +++ b/src/__mocks__/mock-state.ts @@ -11,6 +11,7 @@ export const mockSettings: SettingsState = { participating: false, playSound: true, showNotifications: true, + showBots: true, openAtStartup: false, appearance: Appearance.SYSTEM, colors: false, diff --git a/src/__mocks__/mockedData.ts b/src/__mocks__/mockedData.ts index 987b26c71..7e05086fe 100644 --- a/src/__mocks__/mockedData.ts +++ b/src/__mocks__/mockedData.ts @@ -299,6 +299,7 @@ export const mockedGraphQLResponse: GraphQLSearch = { createdAt: '2022-02-20T18:33:39Z', author: { login: 'comment-user', + type: 'User', }, replies: { nodes: [], @@ -309,6 +310,7 @@ export const mockedGraphQLResponse: GraphQLSearch = { createdAt: '2022-02-21T03:30:42Z', author: { login: 'comment-user', + type: 'User', }, replies: { nodes: [], @@ -319,6 +321,7 @@ export const mockedGraphQLResponse: GraphQLSearch = { createdAt: '2022-02-21T18:26:27Z', author: { login: 'comment-user', + type: 'User', }, replies: { nodes: [ @@ -327,6 +330,7 @@ export const mockedGraphQLResponse: GraphQLSearch = { createdAt: '2022-02-23T00:57:58Z', author: { login: 'reply-user', + type: 'User', }, }, ], @@ -337,6 +341,7 @@ export const mockedGraphQLResponse: GraphQLSearch = { createdAt: '2022-02-23T00:57:49Z', author: { login: 'comment-user', + type: 'User', }, replies: { nodes: [], @@ -347,6 +352,7 @@ export const mockedGraphQLResponse: GraphQLSearch = { createdAt: '2022-02-27T01:22:20Z', author: { login: 'comment-user', + type: 'User', }, replies: { nodes: [ @@ -355,6 +361,7 @@ export const mockedGraphQLResponse: GraphQLSearch = { createdAt: '2022-03-05T17:43:52Z', author: { login: 'reply-user', + type: 'User', }, }, ], @@ -365,6 +372,7 @@ export const mockedGraphQLResponse: GraphQLSearch = { createdAt: '2022-03-04T20:39:44Z', author: { login: 'comment-user', + type: 'User', }, replies: { nodes: [ @@ -373,6 +381,7 @@ export const mockedGraphQLResponse: GraphQLSearch = { createdAt: '2022-03-05T17:41:04Z', author: { login: 'reply-user', + type: 'User', }, }, ], @@ -383,6 +392,7 @@ export const mockedGraphQLResponse: GraphQLSearch = { createdAt: '2022-03-05T11:05:42Z', author: { login: 'comment-user', + type: 'User', }, replies: { nodes: [ @@ -391,6 +401,7 @@ export const mockedGraphQLResponse: GraphQLSearch = { createdAt: '2022-03-05T17:41:44Z', author: { login: 'reply-user', + type: 'User', }, }, ], @@ -412,6 +423,7 @@ export const mockedGraphQLResponse: GraphQLSearch = { createdAt: '2022-02-20T18:33:39Z', author: { login: 'comment-user', + type: 'User', }, replies: { nodes: [], @@ -422,6 +434,7 @@ export const mockedGraphQLResponse: GraphQLSearch = { createdAt: '2022-02-21T03:30:42Z', author: { login: 'comment-user', + type: 'User', }, replies: { nodes: [], @@ -432,6 +445,7 @@ export const mockedGraphQLResponse: GraphQLSearch = { createdAt: '2022-02-21T18:26:27Z', author: { login: 'comment-user', + type: 'User', }, replies: { nodes: [ @@ -440,6 +454,7 @@ export const mockedGraphQLResponse: GraphQLSearch = { createdAt: '2022-02-23T00:57:58Z', author: { login: 'reply-user', + type: 'User', }, }, ], @@ -450,6 +465,7 @@ export const mockedGraphQLResponse: GraphQLSearch = { createdAt: '2022-02-23T00:57:49Z', author: { login: 'comment-user', + type: 'User', }, replies: { nodes: [], @@ -460,6 +476,7 @@ export const mockedGraphQLResponse: GraphQLSearch = { createdAt: '2022-02-27T01:22:20Z', author: { login: 'comment-user', + type: 'User', }, replies: { nodes: [ @@ -468,6 +485,7 @@ export const mockedGraphQLResponse: GraphQLSearch = { createdAt: '2022-03-05T17:43:52Z', author: { login: 'reply-user', + type: 'User', }, }, ], @@ -478,6 +496,7 @@ export const mockedGraphQLResponse: GraphQLSearch = { createdAt: '2022-03-04T20:39:44Z', author: { login: 'comment-user', + type: 'User', }, replies: { nodes: [ @@ -486,6 +505,7 @@ export const mockedGraphQLResponse: GraphQLSearch = { createdAt: '2022-03-05T17:41:04Z', author: { login: 'reply-user', + type: 'User', }, }, ], @@ -496,6 +516,7 @@ export const mockedGraphQLResponse: GraphQLSearch = { createdAt: '2022-03-05T11:05:42Z', author: { login: 'comment-user', + type: 'User', }, replies: { nodes: [ @@ -504,6 +525,7 @@ export const mockedGraphQLResponse: GraphQLSearch = { createdAt: '2022-03-05T17:41:44Z', author: { login: 'reply-user', + type: 'User', }, }, ], diff --git a/src/context/App.tsx b/src/context/App.tsx index 2f7b8487e..5f1fff0c5 100644 --- a/src/context/App.tsx +++ b/src/context/App.tsx @@ -34,6 +34,7 @@ export const defaultSettings: SettingsState = { participating: false, playSound: true, showNotifications: true, + showBots: true, openAtStartup: false, appearance: Appearance.SYSTEM, colors: null, diff --git a/src/hooks/useNotifications.ts b/src/hooks/useNotifications.ts index 72f43459c..415cec1e0 100644 --- a/src/hooks/useNotifications.ts +++ b/src/hooks/useNotifications.ts @@ -128,32 +128,48 @@ export const useNotifications = (colors: boolean): NotificationsState => { data.map(async (accountNotifications) => { return { hostname: accountNotifications.hostname, - notifications: await axios.all( - accountNotifications.notifications.map( - async (notification: Notification) => { - const isEnterprise = isEnterpriseHost( - accountNotifications.hostname, - ); - const token = isEnterprise - ? getEnterpriseAccountToken( - accountNotifications.hostname, - accounts.enterpriseAccounts, - ) - : accounts.token; - - const additionalSubjectDetails = - await getGitifySubjectDetails(notification, token); - - return { - ...notification, - subject: { - ...notification.subject, - ...additionalSubjectDetails, - }, - }; - }, - ), - ), + notifications: await axios + .all( + accountNotifications.notifications.map( + async (notification: Notification) => { + const isEnterprise = isEnterpriseHost( + accountNotifications.hostname, + ); + const token = isEnterprise + ? getEnterpriseAccountToken( + accountNotifications.hostname, + accounts.enterpriseAccounts, + ) + : accounts.token; + + const additionalSubjectDetails = + await getGitifySubjectDetails( + notification, + token, + ); + + return { + ...notification, + subject: { + ...notification.subject, + ...additionalSubjectDetails, + }, + }; + }, + ), + ) + .then((notifications) => { + return notifications.filter((notification) => { + if ( + !settings.showBots && + notification.subject?.user.type === 'Bot' + ) { + return false; + } + + return true; + }); + }), }; }), ) diff --git a/src/routes/Settings.tsx b/src/routes/Settings.tsx index c811940ea..4ead4937e 100644 --- a/src/routes/Settings.tsx +++ b/src/routes/Settings.tsx @@ -151,6 +151,12 @@ export const SettingsRoute: React.FC = () => { updateSetting('showNotifications', evt.target.checked) } /> + updateSetting('showBots', evt.target.checked)} + /> Date: Wed, 27 Mar 2024 06:08:41 -0400 Subject: [PATCH 2/4] feat(settings): ignore bot notifications --- src/context/App.test.tsx | 2 ++ src/context/App.tsx | 2 +- src/hooks/useNotifications.test.ts | 22 +++++++++++++---- .../__snapshots__/Settings.test.tsx.snap | 24 +++++++++++++++++++ 4 files changed, 45 insertions(+), 5 deletions(-) diff --git a/src/context/App.test.tsx b/src/context/App.test.tsx index 6f704f9f1..63d0e8bfa 100644 --- a/src/context/App.test.tsx +++ b/src/context/App.test.tsx @@ -290,6 +290,7 @@ describe('context/App.tsx', () => { participating: true, playSound: true, showNotifications: true, + showBots: true, colors: null, markAsDoneOnOpen: false, }, @@ -328,6 +329,7 @@ describe('context/App.tsx', () => { participating: false, playSound: true, showNotifications: true, + showBots: true, colors: null, markAsDoneOnOpen: false, }, diff --git a/src/context/App.tsx b/src/context/App.tsx index 5f1fff0c5..3da7ba375 100644 --- a/src/context/App.tsx +++ b/src/context/App.tsx @@ -92,7 +92,7 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => { useEffect(() => { fetchNotifications(accounts, settings); - }, [settings.participating]); + }, [settings.participating, settings.showBots]); useEffect(() => { fetchNotifications(accounts, settings); diff --git a/src/hooks/useNotifications.test.ts b/src/hooks/useNotifications.test.ts index 2c9ef002a..ecb4e02f4 100644 --- a/src/hooks/useNotifications.test.ts +++ b/src/hooks/useNotifications.test.ts @@ -291,16 +291,30 @@ describe('hooks/useNotifications.ts', () => { nock('https://api.github.com') .get('/3') - .reply(200, { state: 'closed', merged: true }); + .reply(200, { + state: 'closed', + merged: true, + user: { + login: 'some-user', + type: 'User', + }, + }); nock('https://api.github.com') .get('/3/comments') - .reply(200, { user: { login: 'some-user' } }); + .reply(200, { user: { login: 'some-commenter', type: 'User' } }); nock('https://api.github.com') .get('/4') - .reply(200, { state: 'closed', merged: false }); + .reply(200, { + state: 'closed', + merged: false, + user: { + login: 'some-user', + type: 'User', + }, + }); nock('https://api.github.com') .get('/4/comments') - .reply(200, { user: { login: 'some-user' } }); + .reply(200, { user: { login: 'some-commenter', type: 'User' } }); const { result } = renderHook(() => useNotifications(true)); diff --git a/src/routes/__snapshots__/Settings.test.tsx.snap b/src/routes/__snapshots__/Settings.test.tsx.snap index 6778ba750..dbb031e1e 100644 --- a/src/routes/__snapshots__/Settings.test.tsx.snap +++ b/src/routes/__snapshots__/Settings.test.tsx.snap @@ -230,6 +230,30 @@ exports[`routes/Settings.tsx should render itself & its children 1`] = ` +
+
+ +
+
+ +
+
From 3fcdca15c5fe47050c5218635eb1ef95a84ee73e Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Wed, 27 Mar 2024 06:32:22 -0400 Subject: [PATCH 3/4] feat(settings): ignore bot notifications --- src/routes/Settings.test.tsx | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/routes/Settings.test.tsx b/src/routes/Settings.test.tsx index d0b71382b..3e36c9da1 100644 --- a/src/routes/Settings.test.tsx +++ b/src/routes/Settings.test.tsx @@ -127,6 +127,34 @@ describe('routes/Settings.tsx', () => { expect(updateSetting).toHaveBeenCalledWith('participating', false); }); + it('should toggle the showBots checkbox', async () => { + let getByLabelText; + + await act(async () => { + const { getByLabelText: getByLabelTextLocal } = render( + + + + + , + ); + getByLabelText = getByLabelTextLocal; + }); + + fireEvent.click(getByLabelText('Show notifications from Bot accounts'), { + target: { checked: true }, + }); + + expect(updateSetting).toHaveBeenCalledTimes(1); + expect(updateSetting).toHaveBeenCalledWith('showBots', false); + }); + it('should toggle the playSound checkbox', async () => { let getByLabelText; From ba8d5afe4c628c4239963a127d775946d410455e Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Wed, 27 Mar 2024 06:48:41 -0400 Subject: [PATCH 4/4] feat(settings): ignore bot notifications --- src/hooks/useNotifications.test.ts | 78 ++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/hooks/useNotifications.test.ts b/src/hooks/useNotifications.test.ts index ecb4e02f4..e23a462a8 100644 --- a/src/hooks/useNotifications.test.ts +++ b/src/hooks/useNotifications.test.ts @@ -334,6 +334,84 @@ describe('hooks/useNotifications.ts', () => { expect(result.current.notifications[0].notifications.length).toBe(6); }); }); + + describe('showBots', () => { + it('should hide bot notifications when set to false', async () => { + const accounts: AuthState = { + ...mockAccounts, + enterpriseAccounts: [], + user: mockedUser, + }; + + const notifications = [ + { + id: 1, + subject: { + title: 'This is an Issue.', + type: 'Issue', + url: 'https://api.github.com/1', + latest_comment_url: null, + }, + repository: { + full_name: 'some/repo', + }, + }, + { + id: 2, + subject: { + title: 'This is a Pull Request.', + type: 'PullRequest', + url: 'https://api.github.com/2', + latest_comment_url: null, + }, + repository: { + full_name: 'some/repo', + }, + }, + ]; + + nock('https://api.github.com') + .get('/notifications?participating=false') + .reply(200, notifications); + nock('https://api.github.com') + .get('/1') + .reply(200, { + state: 'closed', + merged: true, + user: { + login: 'some-user', + type: 'User', + }, + }); + nock('https://api.github.com') + .get('/2') + .reply(200, { + state: 'closed', + merged: false, + user: { + login: 'some-bot', + type: 'Bot', + }, + }); + + const { result } = renderHook(() => useNotifications(true)); + + act(() => { + result.current.fetchNotifications(accounts, { + ...mockSettings, + showBots: false, + }); + }); + + expect(result.current.isFetching).toBe(true); + + await waitFor(() => { + expect(result.current.notifications[0].hostname).toBe('github.com'); + }); + + expect(result.current.notifications[0].notifications.length).toBe(1); + }); + }); }); describe('removeNotificationFromState', () => {