Skip to content

Commit e25a532

Browse files
authored
feat: use platform version for github enterprise server feature availability (mark as done) (#1424)
* feat: mark as done for old github enterprise server instances * feat: mark as done for old github enterprise server instances * feat: mark as done for old github enterprise server instances * feat: mark as done for old github enterprise server instances * feat: fetch enterprise server platform version on load and use for feature availability * feat: fetch enterprise server platform version on load and use for feature availability * Merge branch 'main' into feat/mark-as-done-server
1 parent 2f4ee9a commit e25a532

File tree

12 files changed

+147
-40
lines changed

12 files changed

+147
-40
lines changed

src/components/NotificationRow.tsx

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ import { AppContext } from '../context/App';
1010
import { Opacity, Size } from '../types';
1111
import type { Notification } from '../typesGitHub';
1212
import { cn } from '../utils/cn';
13-
import { formatForDisplay } from '../utils/helpers';
13+
import {
14+
formatForDisplay,
15+
isMarkAsDoneFeatureSupported,
16+
} from '../utils/helpers';
1417
import {
1518
getNotificationTypeIcon,
1619
getNotificationTypeIconColor,
@@ -126,16 +129,18 @@ export const NotificationRow: FC<INotificationRow> = ({
126129

127130
{!animateExit && (
128131
<HoverGroup>
129-
<InteractionButton
130-
title="Mark as Done"
131-
icon={CheckIcon}
132-
size={Size.MEDIUM}
133-
onClick={() => {
134-
setAnimateExit(!settings.delayNotificationState);
135-
setShowAsRead(settings.delayNotificationState);
136-
markNotificationDone(notification);
137-
}}
138-
/>
132+
{isMarkAsDoneFeatureSupported(notification.account) && (
133+
<InteractionButton
134+
title="Mark as Done"
135+
icon={CheckIcon}
136+
size={Size.MEDIUM}
137+
onClick={() => {
138+
setAnimateExit(!settings.delayNotificationState);
139+
setShowAsRead(settings.delayNotificationState);
140+
markNotificationDone(notification);
141+
}}
142+
/>
143+
)}
139144
<InteractionButton
140145
title="Mark as Read"
141146
icon={ReadIcon}

src/components/RepositoryNotifications.tsx

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { AppContext } from '../context/App';
1010
import { Opacity, Size } from '../types';
1111
import type { Notification } from '../typesGitHub';
1212
import { cn } from '../utils/cn';
13+
import { isMarkAsDoneFeatureSupported } from '../utils/helpers';
1314
import { openRepository } from '../utils/links';
1415
import { HoverGroup } from './HoverGroup';
1516
import { NotificationRow } from './NotificationRow';
@@ -80,18 +81,20 @@ export const RepositoryNotifications: FC<IRepositoryNotifications> = ({
8081

8182
{!animateExit && (
8283
<HoverGroup>
83-
<InteractionButton
84-
title="Mark Repository as Done"
85-
icon={CheckIcon}
86-
size={Size.MEDIUM}
87-
onClick={(event: MouseEvent<HTMLElement>) => {
88-
// Don't trigger onClick of parent element.
89-
event.stopPropagation();
90-
setAnimateExit(!settings.delayNotificationState);
91-
setShowAsRead(settings.delayNotificationState);
92-
markRepoNotificationsDone(repoNotifications[0]);
93-
}}
94-
/>
84+
{isMarkAsDoneFeatureSupported(repoNotifications[0].account) && (
85+
<InteractionButton
86+
title="Mark Repository as Done"
87+
icon={CheckIcon}
88+
size={Size.MEDIUM}
89+
onClick={(event: MouseEvent<HTMLElement>) => {
90+
// Don't trigger onClick of parent element.
91+
event.stopPropagation();
92+
setAnimateExit(!settings.delayNotificationState);
93+
setShowAsRead(settings.delayNotificationState);
94+
markRepoNotificationsDone(repoNotifications[0]);
95+
}}
96+
/>
97+
)}
9598
<InteractionButton
9699
title="Mark Repository as Read"
97100
icon={ReadIcon}

src/components/settings/NotificationSettings.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ export const NotificationSettings: FC = () => {
5656
onChange={(evt) =>
5757
updateSetting('markAsDoneOnOpen', evt.target.checked)
5858
}
59+
tooltip={
60+
<div>
61+
<strong>Mark as Done</strong> feature is supported in GitHub Cloud
62+
and GitHub Enterprise Server 3.13 or later.
63+
</div>
64+
}
5965
/>
6066
<Checkbox
6167
name="delayNotificationState"

src/context/App.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,13 @@ export const AppProvider = ({ children }: { children: ReactNode }) => {
262262
);
263263
return existing.settings;
264264
}
265+
266+
for (const account of auth.accounts.filter(
267+
(a) => a.platform === 'GitHub Enterprise Server',
268+
)) {
269+
const res = await headNotifications(account.hostname, account.token);
270+
account.version = res.headers['x-github-enterprise-version'];
271+
}
265272
}, []);
266273

267274
const fetchNotificationsWithAccounts = useCallback(

src/hooks/useNotifications.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
markRepositoryNotificationsAsRead,
1414
} from '../utils/api/client';
1515
import { getAccountUUID } from '../utils/auth/utils';
16+
import { isMarkAsDoneFeatureSupported } from '../utils/helpers';
1617
import {
1718
getAllNotifications,
1819
setTrayIconColor,
@@ -120,11 +121,13 @@ export const useNotifications = (): NotificationsState => {
120121
setStatus('loading');
121122

122123
try {
123-
await markNotificationThreadAsDone(
124-
notification.id,
125-
notification.account.hostname,
126-
notification.account.token,
127-
);
124+
if (isMarkAsDoneFeatureSupported(notification.account)) {
125+
await markNotificationThreadAsDone(
126+
notification.id,
127+
notification.account.hostname,
128+
notification.account.token,
129+
);
130+
}
128131

129132
const updatedNotifications = removeNotification(
130133
state.settings,

src/routes/__snapshots__/Settings.test.tsx.snap

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export type Status = 'loading' | 'success' | 'error';
4646
export interface Account {
4747
method: AuthMethod;
4848
platform: PlatformType;
49+
version?: string;
4950
hostname: Hostname;
5051
token: Token;
5152
user: GitifyUser | null;

src/utils/api/__snapshots__/client.test.ts.snap

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/utils/api/client.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ describe('utils/api/client.ts', () => {
135135
expect(axios.defaults.headers.common).toMatchSnapshot();
136136
});
137137

138-
it('should mark notification thread as read- enterprise', async () => {
138+
it('should mark notification thread as read - enterprise', async () => {
139139
await markNotificationThreadAsRead(
140140
mockThreadId,
141141
mockEnterpriseHostname,
@@ -169,7 +169,7 @@ describe('utils/api/client.ts', () => {
169169
expect(axios.defaults.headers.common).toMatchSnapshot();
170170
});
171171

172-
it('should mark notification thread as done- enterprise', async () => {
172+
it('should mark notification thread as done - enterprise', async () => {
173173
await markNotificationThreadAsDone(
174174
mockThreadId,
175175
mockEnterpriseHostname,
@@ -203,7 +203,7 @@ describe('utils/api/client.ts', () => {
203203
expect(axios.defaults.headers.common).toMatchSnapshot();
204204
});
205205

206-
it('should ignore notification thread subscription- enterprise', async () => {
206+
it('should ignore notification thread subscription - enterprise', async () => {
207207
await ignoreNotificationThreadSubscription(
208208
mockThreadId,
209209
mockEnterpriseHostname,

src/utils/api/client.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ export function markNotificationThreadAsRead(
9090
* Marks a thread as "done." Marking a thread as "done" is equivalent to marking a
9191
* notification in your notification inbox on GitHub as done.
9292
*
93+
* NOTE: This was added to GitHub Enterprise Server in version 3.13 or later.
94+
*
9395
* Endpoint documentation: https://docs.github.com/en/rest/activity/notifications#mark-a-thread-as-done
9496
*/
9597
export function markNotificationThreadAsDone(
@@ -99,7 +101,6 @@ export function markNotificationThreadAsDone(
99101
): AxiosPromise<void> {
100102
const url = getGitHubAPIBaseUrl(hostname);
101103
url.pathname += `notifications/threads/${threadId}`;
102-
103104
return apiRequestAuth(url.toString() as Link, 'DELETE', token, {});
104105
}
105106

src/utils/helpers.test.ts

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import type { AxiosPromise, AxiosResponse } from 'axios';
2-
import { mockPersonalAccessTokenAccount } from '../__mocks__/state-mocks';
2+
import {
3+
mockGitHubCloudAccount,
4+
mockGitHubEnterpriseServerAccount,
5+
mockPersonalAccessTokenAccount,
6+
} from '../__mocks__/state-mocks';
37

48
import { defaultSettings } from '../context/App';
59
import type { Hostname, Link, SettingsState } from '../types';
@@ -17,6 +21,7 @@ import {
1721
getFilterCount,
1822
getPlatformFromHostname,
1923
isEnterpriseServerHost,
24+
isMarkAsDoneFeatureSupported,
2025
} from './helpers';
2126

2227
describe('utils/helpers.ts', () => {
@@ -56,6 +61,48 @@ describe('utils/helpers.ts', () => {
5661
});
5762
});
5863

64+
describe('isMarkAsDoneFeatureSupported', () => {
65+
it('should return true for GitHub Cloud', () => {
66+
expect(isMarkAsDoneFeatureSupported(mockGitHubCloudAccount)).toBe(true);
67+
});
68+
69+
it('should return false for GitHub Enterprise Server < v3.13', () => {
70+
const account = {
71+
...mockGitHubEnterpriseServerAccount,
72+
version: '3.12.0',
73+
};
74+
75+
expect(isMarkAsDoneFeatureSupported(account)).toBe(false);
76+
});
77+
78+
it('should return true for GitHub Enterprise Server >= v3.13', () => {
79+
const account = {
80+
...mockGitHubEnterpriseServerAccount,
81+
version: '3.13.3',
82+
};
83+
84+
expect(isMarkAsDoneFeatureSupported(account)).toBe(true);
85+
});
86+
87+
it('should return false for GitHub Enterprise Server when partial version available', () => {
88+
const account = {
89+
...mockGitHubEnterpriseServerAccount,
90+
version: '3',
91+
};
92+
93+
expect(isMarkAsDoneFeatureSupported(account)).toBe(false);
94+
});
95+
96+
it('should return false for GitHub Enterprise Server when no version available', () => {
97+
const account = {
98+
...mockGitHubEnterpriseServerAccount,
99+
version: null,
100+
};
101+
102+
expect(isMarkAsDoneFeatureSupported(account)).toBe(false);
103+
});
104+
});
105+
59106
describe('generateNotificationReferrerId', () => {
60107
it('should generate the notification_referrer_id', () => {
61108
const referrerId = generateNotificationReferrerId(mockSingleNotification);

src/utils/helpers.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { formatDistanceToNowStrict, parseISO } from 'date-fns';
22
import { defaultSettings } from '../context/App';
3-
import type { Hostname, Link, SettingsState } from '../types';
3+
import type { Account, Hostname, Link, SettingsState } from '../types';
44
import type { Notification } from '../typesGitHub';
55
import { getHtmlUrl, getLatestDiscussion } from './api/client';
66
import type { PlatformType } from './auth/types';
@@ -21,6 +21,20 @@ export function isEnterpriseServerHost(hostname: Hostname): boolean {
2121
return !hostname.endsWith(Constants.DEFAULT_AUTH_OPTIONS.hostname);
2222
}
2323

24+
export function isMarkAsDoneFeatureSupported(account: Account): boolean {
25+
if (isEnterpriseServerHost(account.hostname)) {
26+
// Support for "Mark as Done" was added to GitHub Enterprise Server in v3.13 or newer
27+
if (account.version) {
28+
const version = account?.version.split('.').map(Number);
29+
return version[0] >= 3 && version[1] >= 13;
30+
}
31+
32+
return false;
33+
}
34+
35+
return true;
36+
}
37+
2438
export function generateNotificationReferrerId(
2539
notification: Notification,
2640
): string {

0 commit comments

Comments
 (0)