Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support grouping notifications by date or repository #854

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
1 change: 0 additions & 1 deletion src/components/AccountNotifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ export const AccountNotifications = (props: IProps) => {

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

return (
<RepositoryNotifications
key={repoSlug}
Expand Down
6 changes: 6 additions & 0 deletions src/components/NotificationRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const NotificationRow: React.FC<IProps> = ({
markNotification,
markNotificationDone,
unsubscribeNotification,
groupBy,
} = useContext(AppContext);

const pressTitle = useCallback(() => {
Expand Down Expand Up @@ -80,6 +81,11 @@ export const NotificationRow: React.FC<IProps> = ({
onClick={() => pressTitle()}
role="main"
>
{groupBy?.groupType === 'date' && (
<div className="mb-1 text-sm whitespace-nowrap overflow-ellipsis overflow-hidden">
{notification.repository.full_name}
</div>
)}
<div className="mb-1 text-sm whitespace-nowrap overflow-ellipsis overflow-hidden">
{notification.subject.title}
</div>
Expand Down
38 changes: 34 additions & 4 deletions src/components/Repository.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,14 @@ describe('components/Repository.tsx', () => {

it('should render itself & its children', () => {
const tree = TestRenderer.create(
<AppContext.Provider value={{}}>
<AppContext.Provider
value={{
groupBy: {
groupType: 'repository',
setGroupType: () => 'repository',
},
}}
>
<RepositoryNotifications {...props} />
</AppContext.Provider>,
);
Expand All @@ -39,7 +46,14 @@ describe('components/Repository.tsx', () => {

it('should open the browser when clicking on the repo name', () => {
const { getByText } = render(
<AppContext.Provider value={{}}>
<AppContext.Provider
value={{
groupBy: {
groupType: 'repository',
setGroupType: () => 'repository',
},
}}
>
<RepositoryNotifications {...props} />
</AppContext.Provider>,
);
Expand All @@ -54,7 +68,15 @@ describe('components/Repository.tsx', () => {

it('should mark a repo as read', function () {
const { getByTitle } = render(
<AppContext.Provider value={{ markRepoNotifications }}>
<AppContext.Provider
value={{
markRepoNotifications,
groupBy: {
groupType: 'repository',
setGroupType: () => 'repository',
},
}}
>
<RepositoryNotifications {...props} />
</AppContext.Provider>,
);
Expand All @@ -69,7 +91,15 @@ describe('components/Repository.tsx', () => {

it('should mark a repo as done', function () {
const { getByTitle } = render(
<AppContext.Provider value={{ markRepoNotificationsDone }}>
<AppContext.Provider
value={{
markRepoNotificationsDone,
groupBy: {
groupType: 'repository',
setGroupType: () => 'repository',
},
}}
>
<RepositoryNotifications {...props} />
</AppContext.Provider>,
);
Expand Down
48 changes: 25 additions & 23 deletions src/components/Repository.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const RepositoryNotifications: React.FC<IProps> = ({
repoNotifications,
hostname,
}) => {
const { markRepoNotifications, markRepoNotificationsDone } =
const { markRepoNotifications, markRepoNotificationsDone, groupBy } =
useContext(AppContext);

const openBrowser = useCallback(() => {
Expand All @@ -40,32 +40,34 @@ export const RepositoryNotifications: React.FC<IProps> = ({

return (
<>
<div className="flex py-2 px-3 bg-gray-100 dark:bg-gray-darker dark:text-white group">
<div className="flex flex-1 space-x-3 items-center mt-0 text-sm font-medium overflow-hidden overflow-ellipsis whitespace-nowrap">
<img className="rounded w-5 h-5" src={avatarUrl} />
<span onClick={openBrowser}>{repoName}</span>
</div>
{groupBy?.groupType === 'repository' && (
<div className="flex py-2 px-3 bg-gray-100 dark:bg-gray-darker dark:text-white">
<div className="flex flex-1 space-x-3 items-center mt-0 text-sm font-medium overflow-hidden overflow-ellipsis whitespace-nowrap">
<img className="rounded w-5 h-5" src={avatarUrl} />
<span onClick={openBrowser}>{repoName}</span>
</div>

<div className="flex justify-center items-center gap-2 opacity-0 group-hover:opacity-80 transition-opacity">
<button
className="focus:outline-none h-full hover:text-green-500"
title="Mark Repository as Done"
onClick={markRepoAsDone}
>
<CheckIcon size={16} aria-label="Mark Repository as Done" />
</button>
<div className="flex justify-center items-center gap-2">
<button
className="focus:outline-none h-full hover:text-green-500"
title="Mark Repository as Done"
onClick={markRepoAsDone}
>
<CheckIcon size={16} aria-label="Mark Repository as Done" />
</button>

<div className="w-[14px]" />
<div className="w-[14px]" />

<button
className="focus:outline-none h-full hover:text-green-500"
title="Mark Repository as Read"
onClick={markRepoAsRead}
>
<ReadIcon size={14} aria-label="Mark Repository as Read" />
</button>
<button
className="focus:outline-none h-full hover:text-green-500"
title="Mark Repository as Read"
onClick={markRepoAsRead}
>
<ReadIcon size={14} aria-label="Mark Repository as Read" />
</button>
</div>
</div>
</div>
)}

<TransitionGroup>
{repoNotifications.map((obj) => (
Expand Down
44 changes: 41 additions & 3 deletions src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BellIcon } from '@primer/octicons-react';
import { BellIcon, FilterIcon } from '@primer/octicons-react';
import { ipcRenderer } from 'electron';
import React, { useCallback, useContext, useMemo } from 'react';
import React, { useCallback, useContext, useMemo, useState } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';

import { Logo } from '../components/Logo';
Expand All @@ -16,7 +16,9 @@ export const Sidebar: React.FC = () => {
const location = useLocation();

const { isLoggedIn } = useContext(AppContext);
const { notifications, fetchNotifications } = useContext(AppContext);
const { notifications, fetchNotifications, groupBy } = useContext(AppContext);

const [dropdownOpen, setDropdownOpen] = useState<boolean>(false);

const onOpenBrowser = useCallback(() => {
openExternalLink(`https://github.com/${Constants.REPO_SLUG}`);
Expand Down Expand Up @@ -59,6 +61,42 @@ export const Sidebar: React.FC = () => {
<BellIcon size={12} />
{notificationsCount > 0 && notificationsCount}
</div>

<div className="flex items-center">
<div
className={`flex items-center cursor-pointer text-white ${dropdownOpen ? 'z-20' : ''}`}
onClick={() => setDropdownOpen(!dropdownOpen)}
>
<FilterIcon size={12} />
</div>
{dropdownOpen && (
<div
className="fixed inset-0 z-10 flex items-center justify-center"
onClick={() => setDropdownOpen(false)}
>
<div className="absolute top-28 left-0 w-28 border border-gray-300 rounded shadow-md ">
<button
onClick={() => {
groupBy.setGroupType('repository');
setDropdownOpen(false);
}}
className="w-full px-4 py-2 bg-[#161b22] text-white dark:hover:bg-gray-darker rounded text-sm"
>
Repository
</button>
<button
onClick={() => {
groupBy.setGroupType('date');
setDropdownOpen(false);
}}
className="w-full px-4 py-2 bg-[#161b22] text-white dark:hover:bg-gray-darker rounded text-sm"
>
Date
</button>
</div>
</div>
)}
</div>
</div>

<div className="py-4 px-3">
Expand Down
4 changes: 2 additions & 2 deletions src/components/__snapshots__/Repository.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
exports[`components/Repository.tsx should render itself & its children 1`] = `
[
<div
className="flex py-2 px-3 bg-gray-100 dark:bg-gray-darker dark:text-white group"
className="flex py-2 px-3 bg-gray-100 dark:bg-gray-darker dark:text-white"
>
<div
className="flex flex-1 space-x-3 items-center mt-0 text-sm font-medium overflow-hidden overflow-ellipsis whitespace-nowrap"
Expand All @@ -19,7 +19,7 @@ exports[`components/Repository.tsx should render itself & its children 1`] = `
</span>
</div>
<div
className="flex justify-center items-center gap-2 opacity-0 group-hover:opacity-80 transition-opacity"
className="flex justify-center items-center gap-2"
>
<button
className="focus:outline-none h-full hover:text-green-500"
Expand Down
62 changes: 62 additions & 0 deletions src/components/__snapshots__/Sidebar.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,37 @@ exports[`components/Sidebar.tsx should render itself & its children (logged in)
</svg>
4
</div>
<div
className="flex items-center"
>
<div
className="flex items-center cursor-pointer text-white "
onClick={[Function]}
>
<svg
aria-hidden="true"
className="octicon octicon-filter"
fill="currentColor"
focusable="false"
height={12}
role="img"
style={
{
"display": "inline-block",
"overflow": "visible",
"userSelect": "none",
"verticalAlign": "text-bottom",
}
}
viewBox="0 0 16 16"
width={12}
>
<path
d="M.75 3h14.5a.75.75 0 0 1 0 1.5H.75a.75.75 0 0 1 0-1.5ZM3 7.75A.75.75 0 0 1 3.75 7h8.5a.75.75 0 0 1 0 1.5h-8.5A.75.75 0 0 1 3 7.75Zm3 4a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z"
/>
</svg>
</div>
</div>
</div>
<div
className="py-4 px-3"
Expand Down Expand Up @@ -201,6 +232,37 @@ exports[`components/Sidebar.tsx should render itself & its children (logged out)
</svg>
4
</div>
<div
className="flex items-center"
>
<div
className="flex items-center cursor-pointer text-white "
onClick={[Function]}
>
<svg
aria-hidden="true"
className="octicon octicon-filter"
fill="currentColor"
focusable="false"
height={12}
role="img"
style={
{
"display": "inline-block",
"overflow": "visible",
"userSelect": "none",
"verticalAlign": "text-bottom",
}
}
viewBox="0 0 16 16"
width={12}
>
<path
d="M.75 3h14.5a.75.75 0 0 1 0 1.5H.75a.75.75 0 0 1 0-1.5ZM3 7.75A.75.75 0 0 1 3.75 7h8.5a.75.75 0 0 1 0 1.5h-8.5A.75.75 0 0 1 3 7.75Zm3 4a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z"
/>
</svg>
</div>
</div>
</div>
<div
className="py-4 px-3"
Expand Down
16 changes: 16 additions & 0 deletions src/context/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ interface AppContextState {

settings: SettingsState;
updateSetting: (name: keyof SettingsState, value: any) => void;

groupBy: {
groupType: 'repository' | 'date';
setGroupType: (type: 'repository' | 'date') => void;
};
}

export const AppContext = createContext<Partial<AppContextState>>({});
Expand All @@ -81,6 +86,10 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
markRepoNotificationsDone,
} = useNotifications(settings.colors);

const [groupType, setGroupType] = useState<'repository' | 'date'>(
'repository',
);

useEffect(() => {
restoreSettings();
}, []);
Expand Down Expand Up @@ -207,6 +216,11 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
[accounts, notifications],
);

const groupByContextValue = useMemo(
() => ({ groupType, setGroupType }),
[groupType],
);

return (
<AppContext.Provider
value={{
Expand All @@ -230,6 +244,8 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {

settings,
updateSetting,

groupBy: groupByContextValue,
}}
>
{children}
Expand Down