Skip to content

Commit

Permalink
feat: support repo-level transition and delayed notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
setchy committed Jul 12, 2024
1 parent 7192b52 commit 67121b9
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 28 deletions.
4 changes: 4 additions & 0 deletions src/components/NotificationRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ import { NotificationHeader } from './notification/NotificationHeader';

interface INotificationRow {
notification: Notification;
isRead?: boolean;
}

export const NotificationRow: FC<INotificationRow> = ({
notification,
isRead = false,
}: INotificationRow) => {
const {
settings,
Expand Down Expand Up @@ -56,6 +58,7 @@ export const NotificationRow: FC<INotificationRow> = ({
removeNotificationFromState,
settings,
]);

const unsubscribeFromThread = (event: MouseEvent<HTMLElement>) => {
// Don't trigger onClick of parent element.
event.stopPropagation();
Expand Down Expand Up @@ -88,6 +91,7 @@ export const NotificationRow: FC<INotificationRow> = ({
animateExit &&
'translate-x-full opacity-0 transition duration-[350ms] ease-in-out',
showAsRead && Opacity.READ,
isRead && Opacity.READ,
)}
>
<div
Expand Down
10 changes: 7 additions & 3 deletions src/components/RepositoryNotifications.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
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 type { Link } from '../types';
import {
Expand Down Expand Up @@ -55,7 +55,9 @@ describe('components/Repository.tsx', () => {

it('should mark a repo as read', () => {
render(
<AppContext.Provider value={{ markRepoNotificationsRead }}>
<AppContext.Provider
value={{ settings: { ...mockSettings }, markRepoNotificationsRead }}
>
<RepositoryNotifications {...props} />
</AppContext.Provider>,
);
Expand All @@ -69,7 +71,9 @@ describe('components/Repository.tsx', () => {

it('should mark a repo as done', () => {
render(
<AppContext.Provider value={{ markRepoNotificationsDone }}>
<AppContext.Provider
value={{ settings: { ...mockSettings }, markRepoNotificationsDone }}
>
<RepositoryNotifications {...props} />
</AppContext.Provider>,
);
Expand Down
51 changes: 28 additions & 23 deletions src/components/RepositoryNotifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,7 @@ import {
MarkGithubIcon,
ReadIcon,
} from '@primer/octicons-react';
import {
type FC,
type MouseEvent,
useCallback,
useContext,
useState,
} from 'react';
import { type FC, type MouseEvent, useContext, useState } from 'react';
import { AppContext } from '../context/App';
import { Opacity, Size } from '../types';
import type { Notification } from '../typesGitHub';
Expand All @@ -31,22 +25,15 @@ export const RepositoryNotifications: FC<IRepositoryNotifications> = ({
repoName,
repoNotifications,
}) => {
const { markRepoNotificationsRead, markRepoNotificationsDone } =
const { settings, markRepoNotificationsRead, markRepoNotificationsDone } =
useContext(AppContext);

const markRepoAsRead = useCallback(() => {
markRepoNotificationsRead(repoNotifications[0]);
}, [repoNotifications, markRepoNotificationsRead]);

const markRepoAsDone = useCallback(() => {
markRepoNotificationsDone(repoNotifications[0]);
}, [repoNotifications, markRepoNotificationsDone]);

const avatarUrl = repoNotifications[0].repository.owner.avatar_url;

const [animateExit, setAnimateExit] = useState(false);
const [showAsRead, setShowAsRead] = useState(false);
const [showRepositoryNotifications, setShowRepositoryNotifications] =
useState(true);

const avatarUrl = repoNotifications[0].repository.owner.avatar_url;

const toggleRepositoryNotifications = () => {
setShowRepositoryNotifications(!showRepositoryNotifications);
};
Expand All @@ -68,7 +55,9 @@ export const RepositoryNotifications: FC<IRepositoryNotifications> = ({
<div
className={cn(
'flex flex-1 gap-4 items-center overflow-hidden overflow-ellipsis whitespace-nowrap text-sm font-medium',
Opacity.MEDIUM,
animateExit &&
'translate-x-full opacity-0 transition duration-[350ms] ease-in-out',
showAsRead ? Opacity.READ : Opacity.MEDIUM,
)}
>
<AvatarIcon
Expand All @@ -94,13 +83,25 @@ export const RepositoryNotifications: FC<IRepositoryNotifications> = ({
title="Mark Repository as Done"
icon={CheckIcon}
size={Size.MEDIUM}
onClick={markRepoAsDone}
onClick={(event: MouseEvent<HTMLElement>) => {
// Don't trigger onClick of parent element.
event.stopPropagation();
setAnimateExit(!settings.delayNotificationState);
setShowAsRead(settings.delayNotificationState);
markRepoNotificationsDone(repoNotifications[0]);
}}
/>
<InteractionButton
title="Mark Repository as Read"
icon={ReadIcon}
size={Size.SMALL}
onClick={markRepoAsRead}
onClick={(event: MouseEvent<HTMLElement>) => {
// Don't trigger onClick of parent element.
event.stopPropagation();
setAnimateExit(!settings.delayNotificationState);
setShowAsRead(settings.delayNotificationState);
markRepoNotificationsRead(repoNotifications[0]);
}}
/>
<InteractionButton
title={toggleRepositoryNotificationsLabel}
Expand All @@ -113,7 +114,11 @@ export const RepositoryNotifications: FC<IRepositoryNotifications> = ({

{showRepositoryNotifications &&
repoNotifications.map((notification) => (
<NotificationRow key={notification.id} notification={notification} />
<NotificationRow
key={notification.id}
notification={notification}
isRead={showAsRead}
/>
))}
</>
);
Expand Down
5 changes: 4 additions & 1 deletion src/hooks/useNotifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ export const useNotifications = (): NotificationsState => {
);

const markRepoNotificationsRead = useCallback(
async (_state: GitifyState, notification: Notification) => {
async (state: GitifyState, notification: Notification) => {
setStatus('loading');

const repoSlug = notification.repository.full_name;
Expand All @@ -166,7 +166,9 @@ export const useNotifications = (): NotificationsState => {
hostname,
notification.account.token,
);

const updatedNotifications = removeNotifications(
state.settings,
notification,
notifications,
);
Expand Down Expand Up @@ -209,6 +211,7 @@ export const useNotifications = (): NotificationsState => {
}

const updatedNotifications = removeNotifications(
state.settings,
notification,
notifications,
);
Expand Down
15 changes: 15 additions & 0 deletions src/utils/remove-notifications.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
mockAccountNotifications,
mockSingleAccountNotifications,
} from '../__mocks__/notifications-mocks';
import { mockSettings } from '../__mocks__/state-mocks';
import { mockSingleNotification } from './api/__mocks__/response-mocks';
import { removeNotifications } from './remove-notifications';

Expand All @@ -10,6 +11,7 @@ describe('utils/remove-notifications.ts', () => {
expect(mockSingleAccountNotifications[0].notifications.length).toBe(1);

const result = removeNotifications(
mockSettings,
mockSingleNotification,
mockSingleAccountNotifications,
);
Expand All @@ -22,11 +24,24 @@ describe('utils/remove-notifications.ts', () => {
expect(mockAccountNotifications[1].notifications.length).toBe(2);

const result = removeNotifications(
mockSettings,
mockSingleNotification,
mockAccountNotifications,
);

expect(result[0].notifications.length).toBe(0);
expect(result[1].notifications.length).toBe(2);
});

it('should skip notification removal if delay state enabled', () => {
expect(mockSingleAccountNotifications[0].notifications.length).toBe(1);

const result = removeNotifications(
{ ...mockSettings, delayNotificationState: true },
mockSingleNotification,
mockSingleAccountNotifications,
);

expect(result[0].notifications.length).toBe(1);
});
});
7 changes: 6 additions & 1 deletion src/utils/remove-notifications.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import type { AccountNotifications } from '../types';
import type { AccountNotifications, SettingsState } from '../types';
import type { Notification } from '../typesGitHub';
import { getAccountUUID } from './auth/utils';

export function removeNotifications(
settings: SettingsState,
notification: Notification,
notifications: AccountNotifications[],
): AccountNotifications[] {
if (settings.delayNotificationState) {
return notifications;
}

const repoSlug = notification.repository.full_name;

const accountIndex = notifications.findIndex(
Expand Down

0 comments on commit 67121b9

Please sign in to comment.