Skip to content

Commit b7513ef

Browse files
setchyafonsojramos
andauthored
feat(refresh): reset interval and disable manual refresh (#1014)
Co-authored-by: Afonso Jorge Ramos <[email protected]>
1 parent 1cc2e89 commit b7513ef

File tree

5 files changed

+91
-38
lines changed

5 files changed

+91
-38
lines changed

src/components/Sidebar.test.tsx

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { fireEvent, render, screen } from '@testing-library/react';
1+
import { act, fireEvent, render, screen } from '@testing-library/react';
22
import { MemoryRouter } from 'react-router-dom';
33
import * as TestRenderer from 'react-test-renderer';
44
const { shell, ipcRenderer } = require('electron');
55
import { mockSettings } from '../__mocks__/mock-state';
66
import { mockedAccountNotifications } from '../__mocks__/mockedData';
77
import { AppContext } from '../context/App';
8+
import Constants from '../utils/constants';
89
import { Sidebar } from './Sidebar';
910

1011
const mockNavigate = jest.fn();
@@ -19,12 +20,15 @@ describe('components/Sidebar.tsx', () => {
1920
beforeEach(() => {
2021
fetchNotifications.mockReset();
2122

23+
jest.useFakeTimers();
24+
2225
jest.spyOn(ipcRenderer, 'send');
2326
jest.spyOn(shell, 'openExternal');
2427
jest.spyOn(window, 'clearInterval');
2528
});
2629

2730
afterEach(() => {
31+
jest.clearAllTimers();
2832
jest.clearAllMocks();
2933
});
3034

@@ -57,6 +61,37 @@ describe('components/Sidebar.tsx', () => {
5761
expect(tree).toMatchSnapshot();
5862
});
5963

64+
it('should fetch notifications every minute', async () => {
65+
render(
66+
<AppContext.Provider
67+
value={{ isLoggedIn: true, notifications: [], fetchNotifications }}
68+
>
69+
<MemoryRouter>
70+
<Sidebar />
71+
</MemoryRouter>
72+
</AppContext.Provider>,
73+
);
74+
fetchNotifications.mockReset();
75+
76+
act(() => {
77+
jest.advanceTimersByTime(Constants.FETCH_INTERVAL);
78+
return;
79+
});
80+
expect(fetchNotifications).toHaveBeenCalledTimes(1);
81+
82+
act(() => {
83+
jest.advanceTimersByTime(Constants.FETCH_INTERVAL);
84+
return;
85+
});
86+
expect(fetchNotifications).toHaveBeenCalledTimes(2);
87+
88+
act(() => {
89+
jest.advanceTimersByTime(Constants.FETCH_INTERVAL);
90+
return;
91+
});
92+
expect(fetchNotifications).toHaveBeenCalledTimes(3);
93+
});
94+
6095
it('should refresh the notifications', () => {
6196
render(
6297
<AppContext.Provider
@@ -68,8 +103,19 @@ describe('components/Sidebar.tsx', () => {
68103
</AppContext.Provider>,
69104
);
70105
fetchNotifications.mockReset();
71-
fireEvent.click(screen.getByTitle('Refresh Notifications'));
106+
107+
const enabledRefreshButton = 'Refresh Notifications';
108+
109+
fireEvent.click(screen.getByTitle(enabledRefreshButton));
110+
72111
expect(fetchNotifications).toHaveBeenCalledTimes(1);
112+
113+
act(() => {
114+
jest.advanceTimersByTime(Constants.FETCH_INTERVAL);
115+
return;
116+
});
117+
118+
expect(fetchNotifications).toHaveBeenCalledTimes(2);
73119
});
74120

75121
it('go to the settings route', () => {

src/components/Sidebar.tsx

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,14 @@ import {
66
} from '@primer/octicons-react';
77
import { ipcRenderer } from 'electron';
88

9-
import { type FC, useCallback, useContext, useMemo } from 'react';
9+
import {
10+
type FC,
11+
useCallback,
12+
useContext,
13+
useEffect,
14+
useMemo,
15+
useRef,
16+
} from 'react';
1017
import { useLocation, useNavigate } from 'react-router-dom';
1118

1219
import { Logo } from '../components/Logo';
@@ -22,6 +29,38 @@ export const Sidebar: FC = () => {
2229
const { notifications, fetchNotifications, isLoggedIn, isFetching } =
2330
useContext(AppContext);
2431

32+
const useFetchInterval = (callback, delay: number) => {
33+
const savedCallback = useRef(callback);
34+
const intervalRef = useRef(null);
35+
36+
useEffect(() => {
37+
savedCallback.current = callback;
38+
}, [callback]);
39+
40+
useEffect(() => {
41+
if (delay !== null) {
42+
const id = setInterval(savedCallback.current, delay);
43+
intervalRef.current = id;
44+
return () => clearInterval(id);
45+
}
46+
}, [delay]);
47+
48+
const resetFetchInterval = useCallback(() => {
49+
if (intervalRef.current !== null) {
50+
clearInterval(intervalRef.current);
51+
intervalRef.current = setInterval(savedCallback.current, delay);
52+
}
53+
}, [delay]);
54+
55+
return { resetFetchInterval };
56+
};
57+
58+
const { resetFetchInterval } = useFetchInterval(() => {
59+
if (isLoggedIn) {
60+
fetchNotifications();
61+
}
62+
}, Constants.FETCH_INTERVAL);
63+
2564
const onOpenBrowser = useCallback(() => {
2665
openExternalLink(`https://github.com/${Constants.REPO_SLUG}`);
2766
}, []);
@@ -80,6 +119,7 @@ export const Sidebar: FC = () => {
80119
onClick={() => {
81120
navigate('/', { replace: true });
82121
fetchNotifications();
122+
resetFetchInterval();
83123
}}
84124
disabled={isFetching}
85125
>

src/context/App.test.tsx

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -58,36 +58,6 @@ describe('context/App.tsx', () => {
5858
});
5959
});
6060

61-
it('fetch notifications every minute', async () => {
62-
customRender(null);
63-
64-
// Wait for the useEffects, for settings.participating and accounts, to run.
65-
// Those aren't what we're testing
66-
await waitFor(() =>
67-
expect(fetchNotificationsMock).toHaveBeenCalledTimes(1),
68-
);
69-
70-
fetchNotificationsMock.mockReset();
71-
72-
act(() => {
73-
jest.advanceTimersByTime(60000);
74-
return;
75-
});
76-
expect(fetchNotificationsMock).toHaveBeenCalledTimes(1);
77-
78-
act(() => {
79-
jest.advanceTimersByTime(60000);
80-
return;
81-
});
82-
expect(fetchNotificationsMock).toHaveBeenCalledTimes(2);
83-
84-
act(() => {
85-
jest.advanceTimersByTime(60000);
86-
return;
87-
});
88-
expect(fetchNotificationsMock).toHaveBeenCalledTimes(3);
89-
});
90-
9161
it('should call fetchNotifications', async () => {
9262
const TestComponent = () => {
9363
const { fetchNotifications } = useContext(AppContext);

src/context/App.tsx

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
useState,
88
} from 'react';
99

10-
import { useInterval } from '../hooks/useInterval';
1110
import { useNotifications } from '../hooks/useNotifications';
1211
import {
1312
type AccountNotifications,
@@ -111,10 +110,6 @@ export const AppProvider = ({ children }: { children: ReactNode }) => {
111110
accounts.enterpriseAccounts.length,
112111
]);
113112

114-
useInterval(() => {
115-
fetchNotifications(accounts, settings);
116-
}, 60000);
117-
118113
// biome-ignore lint/correctness/useExhaustiveDependencies: We need to update tray title when settings or notifications changes.
119114
useEffect(() => {
120115
const count = getNotificationCount(notifications);

src/utils/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ export const Constants = {
1616
STORAGE_KEY: 'gitify-storage',
1717

1818
ALLREAD_EMOJIS: ['😉', '🎉', '🐯', '🙈', '🎈', '🎊', '👏', '🎪', '🍝'],
19+
20+
FETCH_INTERVAL: 60000,
1921
};
2022

2123
export const Errors: Record<ErrorType, GitifyError> = {

0 commit comments

Comments
 (0)