Skip to content

Commit 02c9cf5

Browse files
authored
refactor: centralize notification removal logic and re-enable mark as done (#2518)
* refactor: centralize notification removal logic and re-enable mark as done - Add shouldRemoveNotificationsFromState helper to centralize the logic for determining if notifications should be removed or kept in-place - Re-enable "Mark as done" button for unread notifications regardless of fetchReadNotifications setting (it still works, just appears as "read" after refresh due to GitHub API limitation) - Simplify useNotifications hook by using removeNotificationsForAccount consistently for both settings - Update tests to reflect new behavior * chore: remove auto-fix from lint-staged biome check * style: fix indentation in remove.test.ts
1 parent 5c848c7 commit 02c9cf5

10 files changed

Lines changed: 208 additions & 328 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@
146146
]
147147
},
148148
"lint-staged": {
149-
"*": "biome check --fix --no-errors-on-unmatched",
149+
"*": "biome check --no-errors-on-unmatched",
150150
"*.{js,ts,tsx}": "pnpm test --findRelatedTests --passWithNoTests --updateSnapshot"
151151
}
152152
}

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

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ describe('renderer/components/notifications/NotificationRow.tsx', () => {
136136
expect(markNotificationsAsDoneMock).toHaveBeenCalledTimes(1);
137137
});
138138

139-
it('should mark as read (not done) when markAsDoneOnOpen is true but fetchReadNotifications is enabled', async () => {
139+
it('should mark as done when markAsDoneOnOpen is true even with fetchReadNotifications enabled', async () => {
140140
const markNotificationsAsReadMock = jest.fn();
141141
const markNotificationsAsDoneMock = jest.fn();
142142

@@ -158,8 +158,8 @@ describe('renderer/components/notifications/NotificationRow.tsx', () => {
158158
await userEvent.click(screen.getByTestId('notification-row'));
159159

160160
expect(links.openNotification).toHaveBeenCalledTimes(1);
161-
expect(markNotificationsAsReadMock).toHaveBeenCalledTimes(1);
162-
expect(markNotificationsAsDoneMock).not.toHaveBeenCalled();
161+
expect(markNotificationsAsDoneMock).toHaveBeenCalledTimes(1);
162+
expect(markNotificationsAsReadMock).not.toHaveBeenCalled();
163163
});
164164

165165
it('should mark notifications as read', async () => {
@@ -216,7 +216,25 @@ describe('renderer/components/notifications/NotificationRow.tsx', () => {
216216
expect(markNotificationsAsDoneMock).toHaveBeenCalledTimes(1);
217217
});
218218

219-
it('should hide mark as done button when fetchReadNotifications is enabled', async () => {
219+
it('should hide mark as done button when notification is already read', async () => {
220+
const readNotification = {
221+
...mockGitifyNotification,
222+
unread: false,
223+
};
224+
225+
const props = {
226+
notification: readNotification,
227+
account: mockGitHubCloudAccount,
228+
};
229+
230+
renderWithAppContext(<NotificationRow {...props} />);
231+
232+
expect(
233+
screen.queryByTestId('notification-mark-as-done'),
234+
).not.toBeInTheDocument();
235+
});
236+
237+
it('should show mark as done button when fetchReadNotifications is enabled and notification is unread', async () => {
220238
const props = {
221239
notification: mockGitifyNotification,
222240
account: mockGitHubCloudAccount,
@@ -227,8 +245,8 @@ describe('renderer/components/notifications/NotificationRow.tsx', () => {
227245
});
228246

229247
expect(
230-
screen.queryByTestId('notification-mark-as-done'),
231-
).not.toBeInTheDocument();
248+
screen.getByTestId('notification-mark-as-done'),
249+
).toBeInTheDocument();
232250
expect(
233251
screen.getByTestId('notification-mark-as-read'),
234252
).toBeInTheDocument();

src/renderer/components/notifications/NotificationRow.tsx

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { cn } from '../../utils/cn';
99
import { isMarkAsDoneFeatureSupported } from '../../utils/features';
1010
import { openNotification } from '../../utils/links';
1111
import { isGroupByDate } from '../../utils/notifications/group';
12+
import { shouldRemoveNotificationsFromState } from '../../utils/notifications/remove';
1213
import { HoverButton } from '../primitives/HoverButton';
1314
import { HoverGroup } from '../primitives/HoverGroup';
1415
import { NotificationFooter } from './NotificationFooter';
@@ -31,17 +32,13 @@ export const NotificationRow: FC<NotificationRowProps> = ({
3132
} = useAppContext();
3233
const [animateExit, setAnimateExit] = useState(false);
3334

35+
const shouldAnimateExit = shouldRemoveNotificationsFromState(settings);
36+
3437
const handleNotification = useCallback(() => {
35-
// Don't animate exit when fetchReadNotifications is enabled
36-
// as notifications will stay in the list with reduced opacity
37-
const shouldAnimateExit =
38-
!settings.delayNotificationState && !settings.fetchReadNotifications;
3938
setAnimateExit(shouldAnimateExit);
4039
openNotification(notification);
4140

42-
// Fall back to mark as read when fetchReadNotifications is enabled
43-
// since marking as done won't work correctly (API limitation)
44-
if (settings.markAsDoneOnOpen && !settings.fetchReadNotifications) {
41+
if (settings.markAsDoneOnOpen) {
4542
markNotificationsAsDone([notification]);
4643
} else {
4744
markNotificationsAsRead([notification]);
@@ -51,19 +48,16 @@ export const NotificationRow: FC<NotificationRowProps> = ({
5148
markNotificationsAsRead,
5249
markNotificationsAsDone,
5350
settings,
51+
shouldAnimateExit,
5452
]);
5553

5654
const actionMarkAsDone = () => {
57-
setAnimateExit(!settings.delayNotificationState);
55+
setAnimateExit(shouldAnimateExit);
5856
markNotificationsAsDone([notification]);
5957
};
6058

6159
const actionMarkAsRead = () => {
62-
// Don't animate exit when fetchReadNotifications is enabled
63-
// as the notification will stay in the list with reduced opacity
64-
if (!settings.fetchReadNotifications) {
65-
setAnimateExit(!settings.delayNotificationState);
66-
}
60+
setAnimateExit(shouldAnimateExit);
6761
markNotificationsAsRead([notification]);
6862
};
6963

@@ -152,7 +146,7 @@ export const NotificationRow: FC<NotificationRowProps> = ({
152146
action={actionMarkAsDone}
153147
enabled={
154148
isMarkAsDoneFeatureSupported(notification.account) &&
155-
!settings.fetchReadNotifications
149+
notification.unread
156150
}
157151
icon={CheckIcon}
158152
label="Mark as done"

src/renderer/components/notifications/RepositoryNotifications.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { cn } from '../../utils/cn';
99
import { isMarkAsDoneFeatureSupported } from '../../utils/features';
1010
import { getChevronDetails } from '../../utils/helpers';
1111
import { openRepository } from '../../utils/links';
12+
import { shouldRemoveNotificationsFromState } from '../../utils/notifications/remove';
1213
import { AvatarWithFallback } from '../avatars/AvatarWithFallback';
1314
import { HoverButton } from '../primitives/HoverButton';
1415
import { HoverGroup } from '../primitives/HoverGroup';
@@ -30,18 +31,15 @@ export const RepositoryNotifications: FC<RepositoryNotificationsProps> = ({
3031
useState(true);
3132

3233
const avatarUrl = repoNotifications[0].repository.owner.avatarUrl;
34+
const shouldAnimateExit = shouldRemoveNotificationsFromState(settings);
3335

3436
const actionMarkAsDone = () => {
35-
setAnimateExit(!settings.delayNotificationState);
37+
setAnimateExit(shouldAnimateExit);
3638
markNotificationsAsDone(repoNotifications);
3739
};
3840

3941
const actionMarkAsRead = () => {
40-
// Don't animate exit when fetchReadNotifications is enabled
41-
// as the notifications will stay in the list with reduced opacity
42-
if (!settings.fetchReadNotifications) {
43-
setAnimateExit(!settings.delayNotificationState);
44-
}
42+
setAnimateExit(shouldAnimateExit);
4543
markNotificationsAsRead(repoNotifications);
4644
};
4745

@@ -107,7 +105,7 @@ export const RepositoryNotifications: FC<RepositoryNotificationsProps> = ({
107105
action={actionMarkAsDone}
108106
enabled={
109107
isMarkAsDoneFeatureSupported(repoNotifications[0].account) &&
110-
!settings.fetchReadNotifications
108+
!areAllRepoNotificationsRead
111109
}
112110
icon={CheckIcon}
113111
label="Mark repository as done"

src/renderer/components/notifications/__snapshots__/NotificationRow.test.tsx.snap

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

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

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

0 commit comments

Comments
 (0)