diff --git a/src/__mocks__/state-mocks.ts b/src/__mocks__/state-mocks.ts index 68278bc84..5226657c3 100644 --- a/src/__mocks__/state-mocks.ts +++ b/src/__mocks__/state-mocks.ts @@ -84,6 +84,7 @@ export const mockSettings: SettingsState = { showAccountHostname: false, delayNotificationState: false, showPills: true, + showNumber: true, keyboardShortcut: true, groupBy: GroupBy.REPOSITORY, filterReasons: [], diff --git a/src/components/NotificationRow.test.tsx b/src/components/NotificationRow.test.tsx index bb781f14a..15a6abb5b 100644 --- a/src/components/NotificationRow.test.tsx +++ b/src/components/NotificationRow.test.tsx @@ -61,6 +61,26 @@ describe('components/NotificationRow.tsx', () => { expect(tree).toMatchSnapshot(); }); + it('should render itself & its children - hide numbers', async () => { + jest + .spyOn(global.Date, 'now') + .mockImplementation(() => new Date('2024').valueOf()); + + const props = { + notification: mockSingleNotification, + account: mockGitHubCloudAccount, + }; + + const tree = render( + + + , + ); + expect(tree).toMatchSnapshot(); + }); + describe('notification interactions', () => { it('should open a notification in the browser - click', () => { const removeNotificationFromState = jest.fn(); diff --git a/src/components/NotificationRow.tsx b/src/components/NotificationRow.tsx index bfcead590..5f635e214 100644 --- a/src/components/NotificationRow.tsx +++ b/src/components/NotificationRow.tsx @@ -71,6 +71,13 @@ export const NotificationRow: FC = ({ notification.subject.type, ]); + const notificationNumber = notification.subject?.number + ? `#${notification.subject.number}` + : ''; + + const notificationTitle = + `${notification.subject.title} ${notificationNumber}`.trim(); + const groupByDate = settings.groupBy === 'DATE'; return ( @@ -102,9 +109,18 @@ export const NotificationRow: FC = ({
{notification.subject.title} + + {notificationNumber} +
diff --git a/src/components/__snapshots__/AccountNotifications.test.tsx.snap b/src/components/__snapshots__/AccountNotifications.test.tsx.snap index 9250db70f..cec4888c3 100644 --- a/src/components/__snapshots__/AccountNotifications.test.tsx.snap +++ b/src/components/__snapshots__/AccountNotifications.test.tsx.snap @@ -121,6 +121,9 @@ exports[`components/AccountNotifications.tsx should render itself (github.com wi title="I am a robot and this is a test!" > I am a robot and this is a test! +
Improve the UI +
I am a robot and this is a test! +
Improve the UI +
I am a robot and this is a test! +
I am a robot and this is a test! +
I am a robot and this is a test! + +
+
+ +
+ Updated +
+
+ 7 years ago +
+
+ + +
+
+ +
+ + + +
+ + + , + "container":
+
+
+ + + + +
+
+
+ I am a robot and this is a test! + +
+
+ +
+ Updated +
+
+ 7 years ago +
+
+ + +
+
+
+
+ + + +
+
+
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`components/NotificationRow.tsx should render itself & its children - hide numbers 1`] = ` +{ + "asFragment": [Function], + "baseElement": +
+
+
+ + + + +
+
+
+ I am a robot and this is a test! +
I am a robot and this is a test! +
{ expect(updateSetting).toHaveBeenCalledWith('showPills', false); }); + it('should toggle show number checkbox', async () => { + await act(async () => { + render( + + + + + , + ); + }); + + await screen.findByLabelText('Show number'); + + fireEvent.click(screen.getByLabelText('Show number')); + + expect(updateSetting).toHaveBeenCalledTimes(1); + expect(updateSetting).toHaveBeenCalledWith('showNumber', false); + }); + it('should toggle account hostname checkbox', async () => { await act(async () => { render( diff --git a/src/components/settings/AppearanceSettings.tsx b/src/components/settings/AppearanceSettings.tsx index 1d0da8e5a..ae0b959b7 100644 --- a/src/components/settings/AppearanceSettings.tsx +++ b/src/components/settings/AppearanceSettings.tsx @@ -1,6 +1,7 @@ import { CheckIcon, CommentIcon, + GitPullRequestIcon, IssueClosedIcon, MilestoneIcon, PaintbrushIcon, @@ -99,6 +100,41 @@ export const AppearanceSettings: FC = () => {
} /> + + settings.detailedNotifications && + updateSetting('showNumber', evt.target.checked) + } + disabled={!settings.detailedNotifications} + tooltip={ +
+
Show GitHub number for:
+
+
    +
  • + + Discussion +
  • +
  • + + Issue +
  • +
  • + + Pull Request +
  • +
+
+
+ ⚠️ This setting requires Detailed Notifications to + be enabled. +
+
+ } + /> { showAccountHostname: false, delayNotificationState: false, showPills: true, + showNumber: true, keyboardShortcut: true, groupBy: 'REPOSITORY', filterReasons: [], @@ -433,6 +434,7 @@ describe('context/App.tsx', () => { showAccountHostname: false, delayNotificationState: false, showPills: true, + showNumber: true, keyboardShortcut: true, groupBy: 'REPOSITORY', filterReasons: [], diff --git a/src/context/App.tsx b/src/context/App.tsx index 05ac5594a..36ea47424 100644 --- a/src/context/App.tsx +++ b/src/context/App.tsx @@ -67,6 +67,7 @@ export const defaultSettings: SettingsState = { showAccountHostname: false, delayNotificationState: false, showPills: true, + showNumber: true, keyboardShortcut: true, groupBy: GroupBy.REPOSITORY, ...defaultFilters, diff --git a/src/routes/__snapshots__/Settings.test.tsx.snap b/src/routes/__snapshots__/Settings.test.tsx.snap index 0e2fa07a7..2241044b9 100644 --- a/src/routes/__snapshots__/Settings.test.tsx.snap +++ b/src/routes/__snapshots__/Settings.test.tsx.snap @@ -231,6 +231,54 @@ exports[`routes/Settings.tsx should render itself & its children 1`] = `
+
+
+
+ +
+
+ +
+
+
diff --git a/src/types.ts b/src/types.ts index 6fe2d25c3..f60c03f7d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -63,6 +63,7 @@ interface AppearanceSettingsState { detailedNotifications: boolean; showAccountHostname: boolean; showPills: boolean; + showNumber: boolean; } interface NotificationSettingsState { diff --git a/src/typesGitHub.ts b/src/typesGitHub.ts index 2fed9d328..39e567236 100644 --- a/src/typesGitHub.ts +++ b/src/typesGitHub.ts @@ -261,6 +261,7 @@ interface GitHubSubject { // This is not in the GitHub API, but we add it to the type to make it easier to work with export interface GitifySubject { + number?: number; state?: StateType; user?: SubjectUser; reviews?: GitifyPullRequestReview[]; @@ -494,6 +495,7 @@ export interface GraphQLSearch { } export interface Discussion { + number: number; title: string; stateReason: DiscussionStateType; isAnswered: boolean; diff --git a/src/utils/api/__mocks__/response-mocks.ts b/src/utils/api/__mocks__/response-mocks.ts index 74097fb37..714635ab5 100644 --- a/src/utils/api/__mocks__/response-mocks.ts +++ b/src/utils/api/__mocks__/response-mocks.ts @@ -419,6 +419,7 @@ export const mockGraphQLResponse: GraphQLSearch = { search: { nodes: [ { + number: 123, title: '1.16.0', isAnswered: false, stateReason: 'OPEN', diff --git a/src/utils/api/graphql/discussions.ts b/src/utils/api/graphql/discussions.ts index b5ce239af..93ec4c91a 100644 --- a/src/utils/api/graphql/discussions.ts +++ b/src/utils/api/graphql/discussions.ts @@ -31,6 +31,7 @@ export const QUERY_SEARCH_DISCUSSIONS = gql` search(query: $queryStatement, type: DISCUSSION, first: $firstDiscussions) { nodes { ... on Discussion { + number title stateReason isAnswered diff --git a/src/utils/subject.test.ts b/src/utils/subject.test.ts index 5b030539f..bb53ac8c9 100644 --- a/src/utils/subject.test.ts +++ b/src/utils/subject.test.ts @@ -221,6 +221,7 @@ describe('utils/subject.ts', () => { const result = await getGitifySubjectDetails(mockNotification); expect(result).toEqual({ + number: 123, state: 'ANSWERED', user: { login: mockDiscussionAuthor.login, @@ -247,6 +248,7 @@ describe('utils/subject.ts', () => { const result = await getGitifySubjectDetails(mockNotification); expect(result).toEqual({ + number: 123, state: 'DUPLICATE', user: { login: mockDiscussionAuthor.login, @@ -273,6 +275,7 @@ describe('utils/subject.ts', () => { const result = await getGitifySubjectDetails(mockNotification); expect(result).toEqual({ + number: 123, state: 'OPEN', user: { login: mockDiscussionAuthor.login, @@ -299,6 +302,7 @@ describe('utils/subject.ts', () => { const result = await getGitifySubjectDetails(mockNotification); expect(result).toEqual({ + number: 123, state: 'OUTDATED', user: { login: mockDiscussionAuthor.login, @@ -325,6 +329,7 @@ describe('utils/subject.ts', () => { const result = await getGitifySubjectDetails(mockNotification); expect(result).toEqual({ + number: 123, state: 'REOPENED', user: { login: mockDiscussionAuthor.login, @@ -351,6 +356,7 @@ describe('utils/subject.ts', () => { const result = await getGitifySubjectDetails(mockNotification); expect(result).toEqual({ + number: 123, state: 'RESOLVED', user: { login: mockDiscussionAuthor.login, @@ -385,6 +391,7 @@ describe('utils/subject.ts', () => { const result = await getGitifySubjectDetails(mockNotification); expect(result).toEqual({ + number: 123, state: 'ANSWERED', user: { login: mockDiscussionAuthor.login, @@ -414,7 +421,12 @@ describe('utils/subject.ts', () => { it('open issue state', async () => { nock('https://api.github.com') .get('/repos/gitify-app/notifications-test/issues/1') - .reply(200, { state: 'open', user: mockAuthor, labels: [] }); + .reply(200, { + number: 123, + state: 'open', + user: mockAuthor, + labels: [], + }); nock('https://api.github.com') .get('/repos/gitify-app/notifications-test/issues/comments/302888448') @@ -423,6 +435,7 @@ describe('utils/subject.ts', () => { const result = await getGitifySubjectDetails(mockNotification); expect(result).toEqual({ + number: 123, state: 'open', user: { login: mockCommenter.login, @@ -437,7 +450,12 @@ describe('utils/subject.ts', () => { it('closed issue state', async () => { nock('https://api.github.com') .get('/repos/gitify-app/notifications-test/issues/1') - .reply(200, { state: 'closed', user: mockAuthor, labels: [] }); + .reply(200, { + number: 123, + state: 'closed', + user: mockAuthor, + labels: [], + }); nock('https://api.github.com') .get('/repos/gitify-app/notifications-test/issues/comments/302888448') @@ -446,6 +464,7 @@ describe('utils/subject.ts', () => { const result = await getGitifySubjectDetails(mockNotification); expect(result).toEqual({ + number: 123, state: 'closed', user: { login: mockCommenter.login, @@ -461,6 +480,7 @@ describe('utils/subject.ts', () => { nock('https://api.github.com') .get('/repos/gitify-app/notifications-test/issues/1') .reply(200, { + number: 123, state: 'closed', state_reason: 'completed', user: mockAuthor, @@ -474,6 +494,7 @@ describe('utils/subject.ts', () => { const result = await getGitifySubjectDetails(mockNotification); expect(result).toEqual({ + number: 123, state: 'completed', user: { login: mockCommenter.login, @@ -489,6 +510,7 @@ describe('utils/subject.ts', () => { nock('https://api.github.com') .get('/repos/gitify-app/notifications-test/issues/1') .reply(200, { + number: 123, state: 'open', state_reason: 'not_planned', user: mockAuthor, @@ -502,6 +524,7 @@ describe('utils/subject.ts', () => { const result = await getGitifySubjectDetails(mockNotification); expect(result).toEqual({ + number: 123, state: 'not_planned', user: { login: mockCommenter.login, @@ -517,6 +540,7 @@ describe('utils/subject.ts', () => { nock('https://api.github.com') .get('/repos/gitify-app/notifications-test/issues/1') .reply(200, { + number: 123, state: 'open', state_reason: 'reopened', user: mockAuthor, @@ -530,6 +554,7 @@ describe('utils/subject.ts', () => { const result = await getGitifySubjectDetails(mockNotification); expect(result).toEqual({ + number: 123, state: 'reopened', user: { login: mockCommenter.login, @@ -547,6 +572,7 @@ describe('utils/subject.ts', () => { nock('https://api.github.com') .get('/repos/gitify-app/notifications-test/issues/1') .reply(200, { + number: 123, state: 'open', draft: false, merged: false, @@ -557,6 +583,7 @@ describe('utils/subject.ts', () => { const result = await getGitifySubjectDetails(mockNotification); expect(result).toEqual({ + number: 123, state: 'open', user: { login: mockAuthor.login, @@ -573,6 +600,7 @@ describe('utils/subject.ts', () => { nock('https://api.github.com') .get('/repos/gitify-app/notifications-test/issues/1') .reply(200, { + number: 123, state: 'open', user: mockAuthor, labels: [{ name: 'enhancement' }], @@ -587,6 +615,7 @@ describe('utils/subject.ts', () => { const result = await getGitifySubjectDetails(mockNotification); expect(result).toEqual({ + number: 123, state: 'open', user: { login: mockCommenter.login, @@ -602,6 +631,7 @@ describe('utils/subject.ts', () => { nock('https://api.github.com') .get('/repos/gitify-app/notifications-test/issues/1') .reply(200, { + number: 123, state: 'open', user: mockAuthor, labels: null, @@ -616,6 +646,7 @@ describe('utils/subject.ts', () => { const result = await getGitifySubjectDetails(mockNotification); expect(result).toEqual({ + number: 123, state: 'open', user: { login: mockCommenter.login, @@ -646,6 +677,7 @@ describe('utils/subject.ts', () => { nock('https://api.github.com') .get('/repos/gitify-app/notifications-test/pulls/1') .reply(200, { + number: 123, state: 'closed', draft: false, merged: false, @@ -664,6 +696,7 @@ describe('utils/subject.ts', () => { const result = await getGitifySubjectDetails(mockNotification); expect(result).toEqual({ + number: 123, state: 'closed', user: { login: mockCommenter.login, @@ -681,6 +714,7 @@ describe('utils/subject.ts', () => { nock('https://api.github.com') .get('/repos/gitify-app/notifications-test/pulls/1') .reply(200, { + number: 123, state: 'open', draft: true, merged: false, @@ -699,6 +733,7 @@ describe('utils/subject.ts', () => { const result = await getGitifySubjectDetails(mockNotification); expect(result).toEqual({ + number: 123, state: 'draft', user: { login: mockCommenter.login, @@ -716,6 +751,7 @@ describe('utils/subject.ts', () => { nock('https://api.github.com') .get('/repos/gitify-app/notifications-test/pulls/1') .reply(200, { + number: 123, state: 'open', draft: false, merged: true, @@ -734,6 +770,7 @@ describe('utils/subject.ts', () => { const result = await getGitifySubjectDetails(mockNotification); expect(result).toEqual({ + number: 123, state: 'merged', user: { login: mockCommenter.login, @@ -751,6 +788,7 @@ describe('utils/subject.ts', () => { nock('https://api.github.com') .get('/repos/gitify-app/notifications-test/pulls/1') .reply(200, { + number: 123, state: 'open', draft: false, merged: false, @@ -769,6 +807,7 @@ describe('utils/subject.ts', () => { const result = await getGitifySubjectDetails(mockNotification); expect(result).toEqual({ + number: 123, state: 'open', user: { login: mockCommenter.login, @@ -789,6 +828,7 @@ describe('utils/subject.ts', () => { nock('https://api.github.com') .get('/repos/gitify-app/notifications-test/pulls/1') .reply(200, { + number: 123, state: 'open', draft: false, merged: false, @@ -803,6 +843,7 @@ describe('utils/subject.ts', () => { const result = await getGitifySubjectDetails(mockNotification); expect(result).toEqual({ + number: 123, state: 'open', user: { login: mockAuthor.login, @@ -822,6 +863,7 @@ describe('utils/subject.ts', () => { nock('https://api.github.com') .get('/repos/gitify-app/notifications-test/pulls/1') .reply(200, { + number: 123, state: 'open', draft: false, merged: false, @@ -836,6 +878,7 @@ describe('utils/subject.ts', () => { const result = await getGitifySubjectDetails(mockNotification); expect(result).toEqual({ + number: 123, state: 'open', user: { login: mockAuthor.login, @@ -912,6 +955,7 @@ describe('utils/subject.ts', () => { nock('https://api.github.com') .get('/repos/gitify-app/notifications-test/pulls/1') .reply(200, { + number: 123, state: 'open', draft: false, merged: false, @@ -932,6 +976,7 @@ describe('utils/subject.ts', () => { const result = await getGitifySubjectDetails(mockNotification); expect(result).toEqual({ + number: 123, state: 'open', user: { login: mockCommenter.login, @@ -949,6 +994,7 @@ describe('utils/subject.ts', () => { nock('https://api.github.com') .get('/repos/gitify-app/notifications-test/pulls/1') .reply(200, { + number: 123, state: 'open', draft: false, merged: false, @@ -969,6 +1015,7 @@ describe('utils/subject.ts', () => { const result = await getGitifySubjectDetails(mockNotification); expect(result).toEqual({ + number: 123, state: 'open', user: { login: mockCommenter.login, @@ -1293,6 +1340,7 @@ function mockDiscussionNode( isAnswered: boolean, ): Discussion { return { + number: 123, title: 'This is a mock discussion', url: 'https://github.com/gitify-app/notifications-test/discussions/1' as Link, stateReason: state, diff --git a/src/utils/subject.ts b/src/utils/subject.ts index ba0c7853b..a4063c0a3 100644 --- a/src/utils/subject.ts +++ b/src/utils/subject.ts @@ -183,6 +183,7 @@ async function getGitifySubjectForDiscussion( } return { + number: discussion.number, state: discussionState, user: discussionUser, comments: discussion.comments.totalCount, @@ -226,6 +227,7 @@ async function getGitifySubjectForIssue( } return { + number: issue.number, state: issue.state_reason ?? issue.state, user: { login: issueCommentUser?.login ?? issue.user.login, @@ -272,6 +274,7 @@ async function getGitifySubjectForPullRequest( const linkedIssues = parseLinkedIssuesFromPr(pr); return { + number: pr.number, state: prState, user: { login: prCommentUser?.login ?? pr.user.login,