Skip to content

Commit 37d3bef

Browse files
setchyafonsojramos
andauthored
feat(settings): show notification count in tray (#945)
* feat: show notification count in tray * feat: update title * feat: update title * test: update snapshots * fix test * fix test * fix test * test: add coverage --------- Co-authored-by: Afonso Jorge Ramos <[email protected]>
1 parent 20ad533 commit 37d3bef

File tree

9 files changed

+106
-7
lines changed

9 files changed

+106
-7
lines changed

main.js

+5
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ menubarApp.on('ready', () => {
7575
}
7676
}
7777
});
78+
ipcMain.on('update-title', (_, title) => {
79+
if (!menubarApp.tray.isDestroyed()) {
80+
menubarApp.tray.setTitle(title);
81+
}
82+
});
7883
ipcMain.on('set-login-item-settings', (event, settings) => {
7984
app.setLoginItemSettings(settings);
8085
});

src/__mocks__/mock-state.ts

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export const mockSettings: SettingsState = {
1212
playSound: true,
1313
showNotifications: true,
1414
showBots: true,
15+
showNotificationsCountInTray: false,
1516
openAtStartup: false,
1617
theme: Theme.SYSTEM,
1718
colors: false,

src/context/App.test.tsx

+12-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { useNotifications } from '../hooks/useNotifications';
88
import * as apiRequests from '../utils/api-requests';
99
import * as comms from '../utils/comms';
1010
import * as storage from '../utils/storage';
11+
import * as notifications from '../utils/notifications';
1112

1213
jest.mock('../hooks/useNotifications');
1314

@@ -35,6 +36,11 @@ describe('context/App.tsx', () => {
3536

3637
describe('api methods', () => {
3738
const apiRequestAuthMock = jest.spyOn(apiRequests, 'apiRequestAuth');
39+
const getNotificationCountMock = jest.spyOn(
40+
notifications,
41+
'getNotificationCount',
42+
);
43+
getNotificationCountMock.mockReturnValue(1);
3844

3945
const fetchNotificationsMock = jest.fn();
4046
const markNotificationMock = jest.fn();
@@ -285,12 +291,13 @@ describe('context/App.tsx', () => {
285291
expect(saveStateMock).toHaveBeenCalledWith(
286292
{ enterpriseAccounts: [], token: null, user: null },
287293
{
288-
theme: 'SYSTEM',
289-
openAtStartup: false,
290294
participating: true,
291295
playSound: true,
292296
showNotifications: true,
293297
showBots: true,
298+
showNotificationsCountInTray: false,
299+
openAtStartup: false,
300+
theme: 'SYSTEM',
294301
colors: null,
295302
markAsDoneOnOpen: false,
296303
},
@@ -324,12 +331,13 @@ describe('context/App.tsx', () => {
324331
expect(saveStateMock).toHaveBeenCalledWith(
325332
{ enterpriseAccounts: [], token: null, user: null },
326333
{
327-
theme: 'SYSTEM',
328-
openAtStartup: true,
329334
participating: false,
330335
playSound: true,
331336
showNotifications: true,
332337
showBots: true,
338+
showNotificationsCountInTray: false,
339+
openAtStartup: true,
340+
theme: 'SYSTEM',
333341
colors: null,
334342
markAsDoneOnOpen: false,
335343
},

src/context/App.tsx

+13-1
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ import {
1919
import { apiRequestAuth } from '../utils/api-requests';
2020
import { setTheme } from '../utils/theme';
2121
import { addAccount, authGitHub, getToken, getUserData } from '../utils/auth';
22-
import { setAutoLaunch } from '../utils/comms';
22+
import { setAutoLaunch, updateTrayTitle } from '../utils/comms';
2323
import Constants from '../utils/constants';
2424
import { generateGitHubAPIUrl } from '../utils/helpers';
2525
import { clearState, loadState, saveState } from '../utils/storage';
26+
import { getNotificationCount } from '../utils/notifications';
2627

2728
const defaultAccounts: AuthState = {
2829
token: null,
@@ -35,6 +36,7 @@ export const defaultSettings: SettingsState = {
3536
playSound: true,
3637
showNotifications: true,
3738
showBots: true,
39+
showNotificationsCountInTray: false,
3840
openAtStartup: false,
3941
theme: Theme.SYSTEM,
4042
colors: null,
@@ -98,6 +100,16 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
98100
fetchNotifications(accounts, settings);
99101
}, [accounts.token, accounts.enterpriseAccounts.length]);
100102

103+
useEffect(() => {
104+
const count = getNotificationCount(notifications);
105+
106+
if (settings.showNotificationsCountInTray && count > 0) {
107+
updateTrayTitle(count.toString());
108+
} else {
109+
updateTrayTitle();
110+
}
111+
}, [settings.showNotificationsCountInTray, notifications]);
112+
101113
useInterval(() => {
102114
fetchNotifications(accounts, settings);
103115
}, 60000);

src/routes/Settings.test.tsx

+33-1
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,9 @@ describe('routes/Settings.tsx', () => {
7171

7272
expect(logoutMock).toHaveBeenCalledTimes(1);
7373

74-
expect(ipcRenderer.send).toHaveBeenCalledTimes(1);
74+
expect(ipcRenderer.send).toHaveBeenCalledTimes(2);
7575
expect(ipcRenderer.send).toHaveBeenCalledWith('update-icon');
76+
expect(ipcRenderer.send).toHaveBeenCalledWith('update-title', '');
7677
expect(mockNavigate).toHaveBeenNthCalledWith(1, -1);
7778
});
7879

@@ -155,6 +156,37 @@ describe('routes/Settings.tsx', () => {
155156
expect(updateSetting).toHaveBeenCalledWith('showBots', false);
156157
});
157158

159+
it('should toggle the showNotificationsCountInTray checkbox', async () => {
160+
let getByLabelText;
161+
162+
await act(async () => {
163+
const { getByLabelText: getByLabelTextLocal } = render(
164+
<AppContext.Provider
165+
value={{
166+
settings: mockSettings,
167+
accounts: mockAccounts,
168+
updateSetting,
169+
}}
170+
>
171+
<MemoryRouter>
172+
<SettingsRoute />
173+
</MemoryRouter>
174+
</AppContext.Provider>,
175+
);
176+
getByLabelText = getByLabelTextLocal;
177+
});
178+
179+
fireEvent.click(getByLabelText('Show notifications count in tray'), {
180+
target: { checked: true },
181+
});
182+
183+
expect(updateSetting).toHaveBeenCalledTimes(1);
184+
expect(updateSetting).toHaveBeenCalledWith(
185+
'showNotificationsCountInTray',
186+
false,
187+
);
188+
});
189+
158190
it('should toggle the playSound checkbox', async () => {
159191
let getByLabelText;
160192

src/routes/Settings.tsx

+14-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ import { AppContext } from '../context/App';
2020
import { Theme } from '../types';
2121
import { apiRequestAuth } from '../utils/api-requests';
2222
import { setTheme } from '../utils/theme';
23-
import { openExternalLink, updateTrayIcon } from '../utils/comms';
23+
import {
24+
openExternalLink,
25+
updateTrayIcon,
26+
updateTrayTitle,
27+
} from '../utils/comms';
2428
import Constants from '../utils/constants';
2529
import { generateGitHubAPIUrl } from '../utils/helpers';
2630

@@ -71,6 +75,7 @@ export const SettingsRoute: React.FC = () => {
7175
logout();
7276
navigate(-1);
7377
updateTrayIcon();
78+
updateTrayTitle();
7479
}, []);
7580

7681
const quitApp = useCallback(() => {
@@ -168,6 +173,14 @@ export const SettingsRoute: React.FC = () => {
168173
<legend id="system" className="font-semibold mt-2 mb-1">
169174
System
170175
</legend>
176+
<FieldCheckbox
177+
name="showNotificationsCountInTray"
178+
label="Show notifications count in tray"
179+
checked={settings.showNotificationsCountInTray}
180+
onChange={(evt) =>
181+
updateSetting('showNotificationsCountInTray', evt.target.checked)
182+
}
183+
/>
171184
<FieldCheckbox
172185
name="showNotifications"
173186
label="Show system notifications"

src/routes/__snapshots__/Settings.test.tsx.snap

+23
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,29 @@ exports[`routes/Settings.tsx should render itself & its children 1`] = `
260260
>
261261
System
262262
</legend>
263+
<div
264+
class="flex items-start mt-1 mb-3"
265+
>
266+
<div
267+
class="flex items-center h-5"
268+
>
269+
<input
270+
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
271+
id="showNotificationsCountInTray"
272+
type="checkbox"
273+
/>
274+
</div>
275+
<div
276+
class="ml-3 text-sm"
277+
>
278+
<label
279+
class="font-medium text-gray-700 dark:text-gray-200"
280+
for="showNotificationsCountInTray"
281+
>
282+
Show notifications count in tray
283+
</label>
284+
</div>
285+
</div>
263286
<div
264287
class="flex items-start mt-1 mb-3"
265288
>

src/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ interface NotificationSettingsState {
2525
interface SystemSettingsState {
2626
playSound: boolean;
2727
openAtStartup: boolean;
28+
showNotificationsCountInTray: boolean;
2829
}
2930

3031
export enum Theme {

src/utils/comms.ts

+4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ export function updateTrayIcon(notificationsLength = 0): void {
2121
}
2222
}
2323

24+
export function updateTrayTitle(title: string = ''): void {
25+
ipcRenderer.send('update-title', title);
26+
}
27+
2428
export function restoreSetting(setting, value): void {
2529
ipcRenderer.send(setting, value);
2630
}

0 commit comments

Comments
 (0)