Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions src/renderer/__helpers__/test-utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,41 @@ export function AppContextProvider({
children,
value = {},
}: AppContextProviderProps) {
const defaultValue: Partial<AppContextState> = useMemo(() => {
const defaultValue: AppContextState = useMemo(() => {
return {
auth: mockAuth,
settings: mockSettings,
isLoggedIn: true,

notifications: [],
notificationCount: 0,
unreadNotificationCount: 0,
hasNotifications: false,
hasUnreadNotifications: false,

status: 'success',
globalError: null,

// Default mock implementations for all required methods
loginWithGitHubApp: jest.fn(),
loginWithOAuthApp: jest.fn(),
loginWithPersonalAccessToken: jest.fn(),
logoutFromAccount: jest.fn(),

fetchNotifications: jest.fn(),
removeAccountNotifications: jest.fn(),

markNotificationsAsRead: jest.fn(),
markNotificationsAsDone: jest.fn(),
unsubscribeNotification: jest.fn(),

clearFilters: jest.fn(),
resetSettings: jest.fn(),
updateSetting: jest.fn(),
updateFilter: jest.fn(),

...value,
} as Partial<AppContextState>;
} as AppContextState;
}, [value]);

return (
Expand Down
1 change: 1 addition & 0 deletions src/renderer/__mocks__/state-mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const mockNotificationSettings: NotificationSettingsState = {
showPills: true,
showNumber: true,
participating: false,
fetchReadNotifications: false,
markAsDoneOnOpen: false,
markAsDoneOnUnsubscribe: false,
delayNotificationState: false,
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const Sidebar: FC = () => {
status,
settings,
auth,
unreadNotificationCount,
notificationCount,
hasUnreadNotifications,
} = useAppContext();

Expand Down Expand Up @@ -89,7 +89,7 @@ export const Sidebar: FC = () => {
<IconButton
aria-label="Notifications"
data-testid="sidebar-notifications"
description={`${unreadNotificationCount} unread notifications ↗`}
description={`${notificationCount} ${settings.fetchReadNotifications ? 'notifications' : 'unread notifications'} ↗`}
icon={BellIcon}
onClick={() => openGitHubNotifications(primaryAccountHostname)}
size="small"
Expand Down
8 changes: 4 additions & 4 deletions src/renderer/components/__snapshots__/Sidebar.test.tsx.snap

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions src/renderer/components/notifications/NotificationRow.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,24 @@ describe('renderer/components/notifications/NotificationRow.tsx', () => {
expect(markNotificationsAsReadMock).toHaveBeenCalledTimes(1);
});

it('should hide mark as read button when notification is already read', async () => {
const readNotification = {
...mockGitifyNotification,
unread: false,
};

const props = {
notification: readNotification,
account: mockGitHubCloudAccount,
};

renderWithAppContext(<NotificationRow {...props} />);

expect(
screen.queryByTestId('notification-mark-as-read'),
).not.toBeInTheDocument();
});

it('should mark notifications as done', async () => {
const markNotificationsAsDoneMock = jest.fn();

Expand Down
36 changes: 25 additions & 11 deletions src/renderer/components/notifications/NotificationRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,16 @@ export const NotificationRow: FC<NotificationRowProps> = ({
const [animateExit, setAnimateExit] = useState(false);

const handleNotification = useCallback(() => {
setAnimateExit(!settings.delayNotificationState);
// Don't animate exit when fetchReadNotifications is enabled
// as notifications will stay in the list with reduced opacity
const shouldAnimateExit =
!settings.delayNotificationState && !settings.fetchReadNotifications;
setAnimateExit(shouldAnimateExit);
openNotification(notification);

if (settings.markAsDoneOnOpen) {
// Fall back to mark as read when fetchReadNotifications is enabled
// since marking as done won't work correctly (API limitation)
if (settings.markAsDoneOnOpen && !settings.fetchReadNotifications) {
markNotificationsAsDone([notification]);
} else {
markNotificationsAsRead([notification]);
Expand All @@ -54,7 +60,11 @@ export const NotificationRow: FC<NotificationRowProps> = ({
};

const actionMarkAsRead = () => {
setAnimateExit(!settings.delayNotificationState);
// Don't animate exit when fetchReadNotifications is enabled
// as the notification will stay in the list with reduced opacity
if (!settings.fetchReadNotifications) {
setAnimateExit(!settings.delayNotificationState);
}
markNotificationsAsRead([notification]);
};

Expand Down Expand Up @@ -137,21 +147,25 @@ export const NotificationRow: FC<NotificationRowProps> = ({

{!animateExit && (
<HoverGroup bgColor="group-hover:bg-gitify-notification-hover">
<HoverButton
action={actionMarkAsDone}
enabled={isMarkAsDoneFeatureSupported(notification.account)}
icon={CheckIcon}
label="Mark as done"
testid="notification-mark-as-done"
/>

<HoverButton
action={actionMarkAsRead}
enabled={!isNotificationRead}
icon={ReadIcon}
label="Mark as read"
testid="notification-mark-as-read"
/>

<HoverButton
action={actionMarkAsDone}
enabled={
isMarkAsDoneFeatureSupported(notification.account) &&
!settings.fetchReadNotifications
}
icon={CheckIcon}
label="Mark as done"
testid="notification-mark-as-done"
/>

<HoverButton
action={actionUnsubscribeFromThread}
icon={BellSlashIcon}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ describe('renderer/components/notifications/RepositoryNotifications.tsx', () =>
repoNotifications: mockGitHubCloudGitifyNotifications,
};

beforeEach(() => {
// Reset mock notification state between tests since it's mutated
for (const n of mockGitHubCloudGitifyNotifications) {
n.unread = true;
}
});

afterEach(() => {
jest.clearAllMocks();
});
Expand Down
28 changes: 17 additions & 11 deletions src/renderer/components/notifications/RepositoryNotifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ export const RepositoryNotifications: FC<RepositoryNotificationsProps> = ({
};

const actionMarkAsRead = () => {
setAnimateExit(!settings.delayNotificationState);
// Don't animate exit when fetchReadNotifications is enabled
// as the notifications will stay in the list with reduced opacity
if (!settings.fetchReadNotifications) {
setAnimateExit(!settings.delayNotificationState);
}
markNotificationsAsRead(repoNotifications);
};

Expand Down Expand Up @@ -91,23 +95,25 @@ export const RepositoryNotifications: FC<RepositoryNotificationsProps> = ({

{!animateExit && (
<HoverGroup bgColor="group-hover:bg-gitify-repository">
<HoverButton
action={actionMarkAsDone}
enabled={isMarkAsDoneFeatureSupported(
repoNotifications[0].account,
)}
icon={CheckIcon}
label="Mark repository as done"
testid="repository-mark-as-done"
/>

<HoverButton
action={actionMarkAsRead}
enabled={!areAllRepoNotificationsRead}
icon={ReadIcon}
label="Mark repository as read"
testid="repository-mark-as-read"
/>

<HoverButton
action={actionMarkAsDone}
enabled={
isMarkAsDoneFeatureSupported(repoNotifications[0].account) &&
!settings.fetchReadNotifications
}
icon={CheckIcon}
label="Mark repository as done"
testid="repository-mark-as-done"
/>

<HoverButton
action={actionToggleRepositoryNotifications}
icon={Chevron.icon}
Expand Down
Loading
Loading