Skip to content
2 changes: 2 additions & 0 deletions src/__mocks__/state-mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
type AuthState,
type GitifyState,
type GitifyUser,
GroupBy,
type Hostname,
type SettingsState,
Theme,
Expand Down Expand Up @@ -84,6 +85,7 @@ export const mockSettings: SettingsState = {
delayNotificationState: false,
showPills: true,
keyboardShortcut: true,
groupBy: GroupBy.REPOSITORY,
};

export const mockState: GitifyState = {
Expand Down
55 changes: 48 additions & 7 deletions src/components/AccountNotifications.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { act, fireEvent, render, screen } from '@testing-library/react';
import { mockGitHubCloudAccount } from '../__mocks__/state-mocks';
import { mockGitHubCloudAccount, mockSettings } from '../__mocks__/state-mocks';
import { AppContext } from '../context/App';
import { GroupBy } from '../types';
import { mockGitHubNotifications } from '../utils/api/__mocks__/response-mocks';
import * as links from '../utils/links';
import { AccountNotifications } from './AccountNotifications';
Expand All @@ -9,14 +11,37 @@ jest.mock('./RepositoryNotifications', () => ({
}));

describe('components/AccountNotifications.tsx', () => {
it('should render itself (github.com with notifications)', () => {
it('should render itself (github.com with notifications) - group by repositories', () => {
const props = {
account: mockGitHubCloudAccount,
notifications: mockGitHubNotifications,
showAccountHostname: true,
};

const tree = render(<AccountNotifications {...props} />);
const tree = render(
<AppContext.Provider
value={{ settings: { ...mockSettings, groupBy: GroupBy.REPOSITORY } }}
>
<AccountNotifications {...props} />
</AppContext.Provider>,
);
expect(tree).toMatchSnapshot();
});

it('should render itself (github.com with notifications) - group by date', () => {
const props = {
account: mockGitHubCloudAccount,
notifications: mockGitHubNotifications,
showAccountHostname: true,
};

const tree = render(
<AppContext.Provider
value={{ settings: { ...mockSettings, groupBy: GroupBy.DATE } }}
>
<AccountNotifications {...props} />
</AppContext.Provider>,
);
expect(tree).toMatchSnapshot();
});

Expand All @@ -27,7 +52,11 @@ describe('components/AccountNotifications.tsx', () => {
showAccountHostname: true,
};

const tree = render(<AccountNotifications {...props} />);
const tree = render(
<AppContext.Provider value={{ settings: mockSettings }}>
<AccountNotifications {...props} />
</AppContext.Provider>,
);
expect(tree).toMatchSnapshot();
});

Expand All @@ -41,7 +70,11 @@ describe('components/AccountNotifications.tsx', () => {
};

await act(async () => {
render(<AccountNotifications {...props} />);
render(
<AppContext.Provider value={{ settings: mockSettings }}>
<AccountNotifications {...props} />
</AppContext.Provider>,
);
});

fireEvent.click(screen.getByTitle('Open Profile'));
Expand All @@ -58,12 +91,20 @@ describe('components/AccountNotifications.tsx', () => {
};

await act(async () => {
render(<AccountNotifications {...props} />);
render(
<AppContext.Provider value={{ settings: mockSettings }}>
<AccountNotifications {...props} />
</AppContext.Provider>,
);
});

fireEvent.click(screen.getByTitle('Hide account notifications'));

const tree = render(<AccountNotifications {...props} />);
const tree = render(
<AppContext.Provider value={{ settings: mockSettings }}>
<AccountNotifications {...props} />
</AppContext.Provider>,
);
expect(tree).toMatchSnapshot();
});
});
34 changes: 23 additions & 11 deletions src/components/AccountNotifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import {
ChevronLeftIcon,
ChevronUpIcon,
} from '@primer/octicons-react';
import { type FC, useState } from 'react';
import { type FC, useContext, useState } from 'react';
import { AppContext } from '../context/App';
import type { Account } from '../types';
import type { Notification } from '../typesGitHub';
import { openAccountProfile } from '../utils/links';
import { NotificationRow } from './NotificationRow';
import { RepositoryNotifications } from './RepositoryNotifications';
import { PlatformIcon } from './icons/PlatformIcon';

Expand All @@ -21,6 +23,8 @@ export const AccountNotifications: FC<IAccountNotifications> = (
) => {
const { account, showAccountHostname, notifications } = props;

const { settings } = useContext(AppContext);

const groupedNotifications = Object.values(
notifications.reduce(
(acc: { [key: string]: Notification[] }, notification) => {
Expand Down Expand Up @@ -54,6 +58,8 @@ export const AccountNotifications: FC<IAccountNotifications> = (
? 'Hide account notifications'
: 'Show account notifications';

const groupByRepository = settings.groupBy === 'REPOSITORY';

return (
<>
{showAccountHostname && (
Expand Down Expand Up @@ -85,18 +91,24 @@ export const AccountNotifications: FC<IAccountNotifications> = (
</div>
)}

{showAccountNotifications &&
Object.values(groupedNotifications).map((repoNotifications) => {
const repoSlug = repoNotifications[0].repository.full_name;
{showAccountNotifications && groupByRepository
? Object.values(groupedNotifications).map((repoNotifications) => {
const repoSlug = repoNotifications[0].repository.full_name;

return (
<RepositoryNotifications
key={repoSlug}
repoName={repoSlug}
repoNotifications={repoNotifications}
return (
<RepositoryNotifications
key={repoSlug}
repoName={repoSlug}
repoNotifications={repoNotifications}
/>
);
})
: notifications.map((notification) => (
<NotificationRow
key={notification.id}
notification={notification}
/>
);
})}
))}
</>
);
};
28 changes: 25 additions & 3 deletions src/components/NotificationRow.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
mockSettings,
} from '../__mocks__/state-mocks';
import { AppContext } from '../context/App';
import type { Link } from '../types';
import { GroupBy, type Link } from '../types';
import type { Milestone, UserType } from '../typesGitHub';
import { mockSingleNotification } from '../utils/api/__mocks__/response-mocks';
import * as comms from '../utils/comms';
Expand All @@ -21,7 +21,7 @@ describe('components/NotificationRow.tsx', () => {
jest.clearAllMocks();
});

it('should render itself & its children', async () => {
it('should render itself & its children - group by date', async () => {
jest
.spyOn(global.Date, 'now')
.mockImplementation(() => new Date('2024').valueOf());
Expand All @@ -32,7 +32,29 @@ describe('components/NotificationRow.tsx', () => {
};

const tree = render(
<AppContext.Provider value={{ settings: mockSettings }}>
<AppContext.Provider
value={{ settings: { ...mockSettings, groupBy: GroupBy.DATE } }}
>
<NotificationRow {...props} />
</AppContext.Provider>,
);
expect(tree).toMatchSnapshot();
});

it('should render itself & its children - group by repositories', async () => {
jest
.spyOn(global.Date, 'now')
.mockImplementation(() => new Date('2024').valueOf());

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

const tree = render(
<AppContext.Provider
value={{ settings: { ...mockSettings, groupBy: GroupBy.REPOSITORY } }}
>
<NotificationRow {...props} />
</AppContext.Provider>,
);
Expand Down
39 changes: 37 additions & 2 deletions src/components/NotificationRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
CommentIcon,
FeedPersonIcon,
IssueClosedIcon,
MarkGithubIcon,
MilestoneIcon,
ReadIcon,
TagIcon,
Expand All @@ -28,7 +29,11 @@ import {
getNotificationTypeIconColor,
getPullRequestReviewIcon,
} from '../utils/icons';
import { openNotification, openUserProfile } from '../utils/links';
import {
openNotification,
openRepository,
openUserProfile,
} from '../utils/links';
import { formatReason } from '../utils/reason';
import { PillButton } from './buttons/PillButton';
import { AvatarIcon } from './icons/AvatarIcon';
Expand Down Expand Up @@ -101,6 +106,11 @@ export const NotificationRow: FC<INotificationRow> = ({
notification.subject.linkedIssues?.length > 1 ? 'issues' : 'issue'
} ${notification.subject?.linkedIssues?.join(', ')}`;

const repoAvatarUrl = notification.repository.owner.avatar_url;
const repoSlug = notification.repository.full_name;

const groupByRepository = settings.groupBy === 'REPOSITORY';

return (
<div
id={notification.id}
Expand All @@ -115,13 +125,38 @@ export const NotificationRow: FC<INotificationRow> = ({
className={cn('mr-3 flex w-5 items-center justify-center', iconColor)}
title={notificationTitle}
>
<NotificationIcon size={16} aria-label={notification.subject.type} />
<NotificationIcon
size={groupByRepository ? 16 : 20}
aria-label={notification.subject.type}
/>
</div>

<div
className="flex-1 overflow-hidden overflow-ellipsis whitespace-nowrap"
onClick={() => handleNotification()}
>
{!groupByRepository && (
<div
className="mb-1 flex items-center gap-1 cursor-pointer truncate text-sm font-medium "
title={repoSlug}
>
<span>
<AvatarIcon
title={repoSlug}
url={repoAvatarUrl}
size="medium"
defaultIcon={MarkGithubIcon}
/>
</span>
<span
className="cursor-pointer truncate opacity-90"
onClick={() => openRepository(notification.repository)}
>
{repoSlug}
</span>
</div>
)}

<div
className="mb-1 cursor-pointer truncate text-sm"
role="main"
Expand Down
Loading