Skip to content

Commit e32f2f5

Browse files
committed
refactor: use GitifyNotification type with transformation layer
Introduce a clean separation between raw GitHub API types and internal Gitify notification types: - Add transform.ts at API boundary to convert raw GitHub responses - Define GitifyNotification, GitifySubject, GitifyRepository, GitifyOwner types with camelCase properties - Update all components, handlers, filters, and utilities to use new types - Update GraphQL schema aliases for consistent property naming - Remove Notification type alias from typesGitHub.ts Closes #828
1 parent 82572b2 commit e32f2f5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+928
-1131
lines changed

src/renderer/__mocks__/notifications-mocks.ts

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
import { Constants } from '../constants';
22
import type {
33
AccountNotifications,
4+
GitifyNotification,
45
GitifyNotificationState,
6+
GitifyRepository,
7+
GitifySubject,
58
Hostname,
9+
Link,
610
} from '../types';
7-
import type {
8-
Notification,
9-
Repository,
10-
Subject,
11-
SubjectType,
12-
} from '../typesGitHub';
11+
import type { SubjectType } from '../typesGitHub';
1312
import {
1413
mockEnterpriseNotifications,
1514
mockGitHubNotifications,
@@ -47,21 +46,21 @@ export function createMockSubject(mocks: {
4746
title?: string;
4847
type?: SubjectType;
4948
state?: GitifyNotificationState;
50-
}): Subject {
49+
}): GitifySubject {
5150
return {
5251
title: mocks.title ?? 'Mock Subject',
5352
type: mocks.type ?? ('Unknown' as SubjectType),
5453
state: mocks.state ?? ('Unknown' as GitifyNotificationState),
5554
url: null,
56-
latest_comment_url: null,
55+
latestCommentUrl: null,
5756
};
5857
}
5958

6059
export function createPartialMockNotification(
61-
subject: Partial<Subject>,
62-
repository?: Partial<Repository>,
63-
): Notification {
64-
const mockNotification: Partial<Notification> = {
60+
subject: Partial<GitifySubject>,
61+
repository?: Partial<GitifyRepository>,
62+
): GitifyNotification {
63+
const mockNotification: Partial<GitifyNotification> = {
6564
account: {
6665
method: 'Personal Access Token',
6766
platform: 'GitHub Cloud',
@@ -70,28 +69,30 @@ export function createPartialMockNotification(
7069
user: mockGitifyUser,
7170
hasRequiredScopes: true,
7271
},
73-
subject: subject as Subject,
72+
subject: subject as GitifySubject,
7473
repository: {
7574
name: 'notifications-test',
76-
full_name: 'gitify-app/notifications-test',
77-
html_url: 'https://github.com/gitify-app/notifications-test',
75+
fullName: 'gitify-app/notifications-test',
76+
htmlUrl: 'https://github.com/gitify-app/notifications-test' as Link,
7877
owner: {
7978
login: 'gitify-app',
79+
avatarUrl: 'https://avatars.githubusercontent.com/u/1' as Link,
80+
type: 'Organization',
8081
},
8182
...repository,
82-
} as Repository,
83+
} as GitifyRepository,
8384
};
8485

85-
return mockNotification as Notification;
86+
return mockNotification as GitifyNotification;
8687
}
8788

8889
export function createMockNotificationForRepoName(
8990
id: string,
9091
repoFullName: string | null,
91-
): Notification {
92+
): GitifyNotification {
9293
return {
9394
id,
94-
repository: repoFullName ? { full_name: repoFullName } : null,
95+
repository: repoFullName ? { fullName: repoFullName } : null,
9596
account: mockGitHubCloudAccount,
96-
} as Notification;
97+
} as GitifyNotification;
9798
}

src/renderer/__mocks__/user-mocks.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { GitifyUser, Link } from '../types';
22
import type { User } from '../typesGitHub';
3+
import type { AuthorFieldsFragment } from '../utils/api/graphql/generated/graphql';
34

45
export const mockGitifyUser: GitifyUser = {
56
login: 'octocat',
@@ -18,3 +19,13 @@ export function createPartialMockUser(login: string): User {
1819

1920
return mockUser as User;
2021
}
22+
23+
export function createMockAuthorFragment(login: string): AuthorFieldsFragment {
24+
return {
25+
__typename: 'User',
26+
login: login,
27+
htmlUrl: `https://github.com/${login}`,
28+
avatarUrl: 'https://avatars.githubusercontent.com/u/583231?v=4',
29+
type: 'User',
30+
};
31+
}

src/renderer/components/metrics/MetricGroup.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,12 @@ import {
88
} from '@primer/octicons-react';
99

1010
import { useAppContext } from '../../context/App';
11-
import { IconColor } from '../../types';
12-
import type { Notification } from '../../typesGitHub';
11+
import { type GitifyNotification, IconColor } from '../../types';
1312
import { getPullRequestReviewIcon } from '../../utils/icons';
1413
import { MetricPill } from './MetricPill';
1514

1615
interface MetricGroupProps {
17-
notification: Notification;
16+
notification: GitifyNotification;
1817
}
1918

2019
export const MetricGroup: FC<MetricGroupProps> = ({

src/renderer/components/notifications/AccountNotifications.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@ import { GitPullRequestIcon, IssueOpenedIcon } from '@primer/octicons-react';
44
import { Button, Stack } from '@primer/react';
55

66
import { useAppContext } from '../../context/App';
7-
import { type Account, type GitifyError, Size } from '../../types';
8-
import type { Notification } from '../../typesGitHub';
7+
import {
8+
type Account,
9+
type GitifyError,
10+
type GitifyNotification,
11+
Size,
12+
} from '../../types';
913
import { hasMultipleAccounts } from '../../utils/auth/utils';
1014
import { cn } from '../../utils/cn';
1115
import { getChevronDetails } from '../../utils/helpers';
@@ -28,7 +32,7 @@ import { RepositoryNotifications } from './RepositoryNotifications';
2832

2933
interface AccountNotificationsProps {
3034
account: Account;
31-
notifications: Notification[];
35+
notifications: GitifyNotification[];
3236
error: GitifyError | null;
3337
showAccountHeader: boolean;
3438
}

src/renderer/components/notifications/NotificationFooter.test.tsx

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,6 @@ describe('renderer/components/notifications/NotificationFooter.tsx', () => {
2828
expect(tree).toMatchSnapshot();
2929
});
3030

31-
it('should render itself & its children when last_read_at is null', async () => {
32-
const mockNotification = mockSingleNotification;
33-
mockNotification.last_read_at = null;
34-
35-
const props = {
36-
notification: mockNotification,
37-
};
38-
39-
const tree = renderWithAppContext(<NotificationFooter {...props} />);
40-
41-
expect(tree).toMatchSnapshot();
42-
});
43-
4431
describe('security alerts should use github icon for avatar', () => {
4532
it('Repository Dependabot Alerts Thread', async () => {
4633
const mockNotification = mockSingleNotification;
@@ -94,8 +81,8 @@ describe('renderer/components/notifications/NotificationFooter.tsx', () => {
9481
...mockSingleNotification.subject,
9582
user: {
9683
login: 'some-user',
97-
html_url: 'https://github.com/some-user' as Link,
98-
avatar_url:
84+
htmlUrl: 'https://github.com/some-user' as Link,
85+
avatarUrl:
9986
'https://avatars.githubusercontent.com/u/123456789?v=4' as Link,
10087
type: 'User' as UserType,
10188
},
@@ -111,7 +98,7 @@ describe('renderer/components/notifications/NotificationFooter.tsx', () => {
11198

11299
expect(openExternalLinkSpy).toHaveBeenCalledTimes(1);
113100
expect(openExternalLinkSpy).toHaveBeenCalledWith(
114-
props.notification.subject.user.html_url,
101+
props.notification.subject.user.htmlUrl,
115102
);
116103
});
117104
});

src/renderer/components/notifications/NotificationFooter.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,15 @@ import type { FC, MouseEvent } from 'react';
22

33
import { RelativeTime, Stack, Text } from '@primer/react';
44

5-
import { Opacity, Size } from '../../types';
6-
import type { Notification } from '../../typesGitHub';
5+
import { type GitifyNotification, Opacity, Size } from '../../types';
76
import { cn } from '../../utils/cn';
87
import { openUserProfile } from '../../utils/links';
98
import { getReasonDetails } from '../../utils/reason';
109
import { AvatarWithFallback } from '../avatars/AvatarWithFallback';
1110
import { MetricGroup } from '../metrics/MetricGroup';
1211

1312
interface NotificationFooterProps {
14-
notification: Notification;
13+
notification: GitifyNotification;
1514
}
1615

1716
export const NotificationFooter: FC<NotificationFooterProps> = ({
@@ -41,7 +40,7 @@ export const NotificationFooter: FC<NotificationFooterProps> = ({
4140
<AvatarWithFallback
4241
alt={notification.subject.user.login}
4342
size={Size.SMALL}
44-
src={notification.subject.user.avatar_url}
43+
src={notification.subject.user.avatarUrl}
4544
userType={notification.subject.user.type}
4645
/>
4746
</button>
@@ -61,7 +60,7 @@ export const NotificationFooter: FC<NotificationFooterProps> = ({
6160
<Text className="pr-1" title={reason.description}>
6261
{reason.title}
6362
</Text>
64-
<RelativeTime datetime={notification.updated_at} />
63+
<RelativeTime datetime={notification.updatedAt} />
6564
</Stack>
6665

6766
<MetricGroup notification={notification} />

src/renderer/components/notifications/NotificationHeader.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ describe('renderer/components/notifications/NotificationHeader.tsx', () => {
8787

8888
expect(openExternalLinkSpy).toHaveBeenCalledTimes(1);
8989
expect(openExternalLinkSpy).toHaveBeenCalledWith(
90-
props.notification.repository.html_url,
90+
props.notification.repository.htmlUrl,
9191
);
9292
});
9393
});

src/renderer/components/notifications/NotificationHeader.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,21 @@ import type { FC, MouseEvent } from 'react';
33
import { Stack } from '@primer/react';
44

55
import { useAppContext } from '../../context/App';
6-
import { GroupBy, Opacity, Size } from '../../types';
7-
import type { Notification } from '../../typesGitHub';
6+
import { type GitifyNotification, GroupBy, Opacity, Size } from '../../types';
87
import { cn } from '../../utils/cn';
98
import { openRepository } from '../../utils/links';
109
import { AvatarWithFallback } from '../avatars/AvatarWithFallback';
1110

1211
interface NotificationHeaderProps {
13-
notification: Notification;
12+
notification: GitifyNotification;
1413
}
1514

1615
export const NotificationHeader: FC<NotificationHeaderProps> = ({
1716
notification,
1817
}: NotificationHeaderProps) => {
1918
const { settings } = useAppContext();
2019

21-
const repoSlug = notification.repository.full_name;
20+
const repoSlug = notification.repository.fullName;
2221

2322
const notificationNumber = notification.subject?.number
2423
? `#${notification.subject.number}`
@@ -45,7 +44,7 @@ export const NotificationHeader: FC<NotificationHeaderProps> = ({
4544
alt={repoSlug}
4645
name={repoSlug}
4746
size={Size.SMALL}
48-
src={notification.repository.owner.avatar_url}
47+
src={notification.repository.owner.avatarUrl}
4948
userType={notification.repository.owner.type}
5049
/>
5150
</button>

src/renderer/components/notifications/NotificationRow.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ import { BellSlashIcon, CheckIcon, ReadIcon } from '@primer/octicons-react';
44
import { Stack, Text, Tooltip } from '@primer/react';
55

66
import { useAppContext } from '../../context/App';
7-
import { GroupBy, Opacity, Size } from '../../types';
8-
import type { Notification } from '../../typesGitHub';
7+
import { type GitifyNotification, GroupBy, Opacity, Size } from '../../types';
98
import { cn } from '../../utils/cn';
109
import { isMarkAsDoneFeatureSupported } from '../../utils/features';
1110
import { openNotification } from '../../utils/links';
@@ -16,7 +15,7 @@ import { NotificationFooter } from './NotificationFooter';
1615
import { NotificationHeader } from './NotificationHeader';
1716

1817
interface NotificationRowProps {
19-
notification: Notification;
18+
notification: GitifyNotification;
2019
isAnimated?: boolean;
2120
}
2221

src/renderer/components/notifications/RepositoryNotifications.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ describe('renderer/components/notifications/RepositoryNotifications.tsx', () =>
8585
});
8686

8787
it('should use default repository icon when avatar is not available', () => {
88-
props.repoNotifications[0].repository.owner.avatar_url = '' as Link;
88+
props.repoNotifications[0].repository.owner.avatarUrl = '' as Link;
8989

9090
const tree = renderWithAppContext(<RepositoryNotifications {...props} />);
9191

0 commit comments

Comments
 (0)