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(settings): show notification count in tray #945

Merged
merged 12 commits into from
Apr 2, 2024
5 changes: 5 additions & 0 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ menubarApp.on('ready', () => {
}
}
});
ipcMain.on('update-title', (_, title) => {
if (!menubarApp.tray.isDestroyed()) {
menubarApp.tray.setTitle(title);
}
});
ipcMain.on('set-login-item-settings', (event, settings) => {
app.setLoginItemSettings(settings);
});
Expand Down
1 change: 1 addition & 0 deletions src/__mocks__/mock-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const mockSettings: SettingsState = {
playSound: true,
showNotifications: true,
showBots: true,
showNotificationsCountInTray: false,
openAtStartup: false,
theme: Theme.SYSTEM,
colors: false,
Expand Down
16 changes: 12 additions & 4 deletions src/context/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useNotifications } from '../hooks/useNotifications';
import * as apiRequests from '../utils/api-requests';
import * as comms from '../utils/comms';
import * as storage from '../utils/storage';
import * as notifications from '../utils/notifications';

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

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

describe('api methods', () => {
const apiRequestAuthMock = jest.spyOn(apiRequests, 'apiRequestAuth');
const getNotificationCountMock = jest.spyOn(
notifications,
'getNotificationCount',
);
getNotificationCountMock.mockReturnValue(1);

const fetchNotificationsMock = jest.fn();
const markNotificationMock = jest.fn();
Expand Down Expand Up @@ -285,12 +291,13 @@ describe('context/App.tsx', () => {
expect(saveStateMock).toHaveBeenCalledWith(
{ enterpriseAccounts: [], token: null, user: null },
{
theme: 'SYSTEM',
openAtStartup: false,
participating: true,
playSound: true,
showNotifications: true,
showBots: true,
showNotificationsCountInTray: false,
openAtStartup: false,
theme: 'SYSTEM',
colors: null,
markAsDoneOnOpen: false,
},
Expand Down Expand Up @@ -324,12 +331,13 @@ describe('context/App.tsx', () => {
expect(saveStateMock).toHaveBeenCalledWith(
{ enterpriseAccounts: [], token: null, user: null },
{
theme: 'SYSTEM',
openAtStartup: true,
participating: false,
playSound: true,
showNotifications: true,
showBots: true,
showNotificationsCountInTray: false,
openAtStartup: true,
theme: 'SYSTEM',
colors: null,
markAsDoneOnOpen: false,
},
Expand Down
14 changes: 13 additions & 1 deletion src/context/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ import {
import { apiRequestAuth } from '../utils/api-requests';
import { setTheme } from '../utils/theme';
import { addAccount, authGitHub, getToken, getUserData } from '../utils/auth';
import { setAutoLaunch } from '../utils/comms';
import { setAutoLaunch, updateTrayTitle } from '../utils/comms';
import Constants from '../utils/constants';
import { generateGitHubAPIUrl } from '../utils/helpers';
import { clearState, loadState, saveState } from '../utils/storage';
import { getNotificationCount } from '../utils/notifications';

const defaultAccounts: AuthState = {
token: null,
Expand All @@ -35,6 +36,7 @@ export const defaultSettings: SettingsState = {
playSound: true,
showNotifications: true,
showBots: true,
showNotificationsCountInTray: false,
openAtStartup: false,
theme: Theme.SYSTEM,
colors: null,
Expand Down Expand Up @@ -98,6 +100,16 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
fetchNotifications(accounts, settings);
}, [accounts.token, accounts.enterpriseAccounts.length]);

useEffect(() => {
const count = getNotificationCount(notifications);

if (settings.showNotificationsCountInTray && count > 0) {
updateTrayTitle(count.toString());
} else {
updateTrayTitle();
}
}, [settings.showNotificationsCountInTray, notifications]);

useInterval(() => {
fetchNotifications(accounts, settings);
}, 60000);
Expand Down
34 changes: 33 additions & 1 deletion src/routes/Settings.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,9 @@ describe('routes/Settings.tsx', () => {

expect(logoutMock).toHaveBeenCalledTimes(1);

expect(ipcRenderer.send).toHaveBeenCalledTimes(1);
expect(ipcRenderer.send).toHaveBeenCalledTimes(2);
expect(ipcRenderer.send).toHaveBeenCalledWith('update-icon');
expect(ipcRenderer.send).toHaveBeenCalledWith('update-title', '');
expect(mockNavigate).toHaveBeenNthCalledWith(1, -1);
});

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

it('should toggle the showNotificationsCountInTray checkbox', async () => {
let getByLabelText;

await act(async () => {
const { getByLabelText: getByLabelTextLocal } = render(
<AppContext.Provider
value={{
settings: mockSettings,
accounts: mockAccounts,
updateSetting,
}}
>
<MemoryRouter>
<SettingsRoute />
</MemoryRouter>
</AppContext.Provider>,
);
getByLabelText = getByLabelTextLocal;
});

fireEvent.click(getByLabelText('Show notifications count in tray'), {
target: { checked: true },
});

expect(updateSetting).toHaveBeenCalledTimes(1);
expect(updateSetting).toHaveBeenCalledWith(
'showNotificationsCountInTray',
false,
);
});

it('should toggle the playSound checkbox', async () => {
let getByLabelText;

Expand Down
15 changes: 14 additions & 1 deletion src/routes/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ import { AppContext } from '../context/App';
import { Theme } from '../types';
import { apiRequestAuth } from '../utils/api-requests';
import { setTheme } from '../utils/theme';
import { openExternalLink, updateTrayIcon } from '../utils/comms';
import {
openExternalLink,
updateTrayIcon,
updateTrayTitle,
} from '../utils/comms';
import Constants from '../utils/constants';
import { generateGitHubAPIUrl } from '../utils/helpers';

Expand Down Expand Up @@ -71,6 +75,7 @@ export const SettingsRoute: React.FC = () => {
logout();
navigate(-1);
updateTrayIcon();
updateTrayTitle();
}, []);

const quitApp = useCallback(() => {
Expand Down Expand Up @@ -168,6 +173,14 @@ export const SettingsRoute: React.FC = () => {
<legend id="system" className="font-semibold mt-2 mb-1">
System
</legend>
<FieldCheckbox
name="showNotificationsCountInTray"
label="Show notifications count in tray"
checked={settings.showNotificationsCountInTray}
onChange={(evt) =>
updateSetting('showNotificationsCountInTray', evt.target.checked)
}
/>
<FieldCheckbox
name="showNotifications"
label="Show system notifications"
Expand Down
23 changes: 23 additions & 0 deletions src/routes/__snapshots__/Settings.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,29 @@ exports[`routes/Settings.tsx should render itself & its children 1`] = `
>
System
</legend>
<div
class="flex items-start mt-1 mb-3"
>
<div
class="flex items-center h-5"
>
<input
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
id="showNotificationsCountInTray"
type="checkbox"
/>
</div>
<div
class="ml-3 text-sm"
>
<label
class="font-medium text-gray-700 dark:text-gray-200"
for="showNotificationsCountInTray"
>
Show notifications count in tray
</label>
</div>
</div>
<div
class="flex items-start mt-1 mb-3"
>
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ interface NotificationSettingsState {
interface SystemSettingsState {
playSound: boolean;
openAtStartup: boolean;
showNotificationsCountInTray: boolean;
}

export enum Theme {
Expand Down
4 changes: 4 additions & 0 deletions src/utils/comms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export function updateTrayIcon(notificationsLength = 0): void {
}
}

export function updateTrayTitle(title: string = ''): void {
ipcRenderer.send('update-title', title);
}

export function restoreSetting(setting, value): void {
ipcRenderer.send(setting, value);
}