diff --git a/src/renderer/__mocks__/state-mocks.ts b/src/renderer/__mocks__/state-mocks.ts index a12d6ce96..63ea4921e 100644 --- a/src/renderer/__mocks__/state-mocks.ts +++ b/src/renderer/__mocks__/state-mocks.ts @@ -101,6 +101,7 @@ const mockSystemSettings: SystemSettingsState = { showNotificationsCountInTray: true, showNotifications: true, playSound: true, + notificationVolume: 20, useAlternateIdleIcon: false, openAtStartup: false, }; diff --git a/src/renderer/components/icons/VolumeDownIcon.tsx b/src/renderer/components/icons/VolumeDownIcon.tsx new file mode 100644 index 000000000..93b2910a4 --- /dev/null +++ b/src/renderer/components/icons/VolumeDownIcon.tsx @@ -0,0 +1,18 @@ +import type { FC } from 'react'; + +export const VolumeDownIcon: FC = () => ( + + + + + + +); diff --git a/src/renderer/components/icons/VolumeUpIcon.tsx b/src/renderer/components/icons/VolumeUpIcon.tsx new file mode 100644 index 000000000..84aac91a4 --- /dev/null +++ b/src/renderer/components/icons/VolumeUpIcon.tsx @@ -0,0 +1,16 @@ +import type { FC } from 'react'; + +export const VolumeUpIcon: FC = () => ( + + + + +); diff --git a/src/renderer/components/settings/SystemSettings.tsx b/src/renderer/components/settings/SystemSettings.tsx index 58745f203..ab224cba4 100644 --- a/src/renderer/components/settings/SystemSettings.tsx +++ b/src/renderer/components/settings/SystemSettings.tsx @@ -1,15 +1,24 @@ import { type FC, useContext } from 'react'; -import { DeviceDesktopIcon } from '@primer/octicons-react'; -import { Box, Stack, Text } from '@primer/react'; +import { DeviceDesktopIcon, SyncIcon } from '@primer/octicons-react'; +import { + Box, + Button, + ButtonGroup, + IconButton, + Stack, + Text, +} from '@primer/react'; import { APPLICATION } from '../../../shared/constants'; import { isLinux, isMacOS } from '../../../shared/platform'; -import { AppContext } from '../../context/App'; +import { AppContext, defaultSettings } from '../../context/App'; import { OpenPreference } from '../../types'; import { Constants } from '../../utils/constants'; import { Checkbox } from '../fields/Checkbox'; import { RadioGroup } from '../fields/RadioGroup'; +import { VolumeDownIcon } from '../icons/VolumeDownIcon'; +import { VolumeUpIcon } from '../icons/VolumeUpIcon'; import { Title } from '../primitives/Title'; export const SystemSettings: FC = () => { @@ -70,12 +79,72 @@ export const SystemSettings: FC = () => { } /> - updateSetting('playSound', evt.target.checked)} - /> + + + updateSetting('playSound', evt.target.checked)} + /> + + + { + const newVolume = Math.max( + settings.notificationVolume - 10, + 0, + ); + updateSetting('notificationVolume', newVolume); + }} + data-testid="settings-volume-down" + /> + + + {settings.notificationVolume.toFixed(0)}% + + + { + const newVolume = Math.min( + settings.notificationVolume + 10, + 100, + ); + updateSetting('notificationVolume', newVolume); + }} + data-testid="settings-volume-up" + /> + + { + updateSetting( + 'notificationVolume', + defaultSettings.notificationVolume, + ); + }} + data-testid="settings-volume-reset" + /> + + + - - - Play sound - + + + + Play sound + + + + + + + + + + + + + + + 20 + % + + + + + + + + + + + + + + + + { expect(window.Audio.prototype.play).toHaveBeenCalledTimes(1); }); }); + + describe('triggerNativeNotifications', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should raise only sound notification with correct volume', () => { + const settings: SettingsState = { + ...defaultSettings, + playSound: true, + showNotifications: false, + notificationVolume: 80, + }; + + const raiseSoundNotificationMock = jest.spyOn( + native, + 'raiseSoundNotification', + ); + jest.spyOn(native, 'raiseNativeNotification'); + + native.triggerNativeNotifications([], mockAccountNotifications, { + auth: mockAuth, + settings, + }); + + expect(raiseSoundNotificationMock).toHaveBeenCalledWith(0.8); + expect(native.raiseNativeNotification).not.toHaveBeenCalled(); + }); + }); }); diff --git a/src/renderer/utils/notifications/native.ts b/src/renderer/utils/notifications/native.ts index cda7f6367..cce0f4614 100644 --- a/src/renderer/utils/notifications/native.ts +++ b/src/renderer/utils/notifications/native.ts @@ -2,6 +2,7 @@ import path from 'node:path'; import { APPLICATION } from '../../../shared/constants'; import { isWindows } from '../../../shared/platform'; +import { defaultSettings } from '../../context/App'; import type { AccountNotifications, GitifyState } from '../../types'; import { Notification } from '../../typesGitHub'; import { getAccountUUID } from '../auth/utils'; @@ -48,7 +49,7 @@ export const triggerNativeNotifications = ( } if (state.settings.playSound) { - raiseSoundNotification(); + raiseSoundNotification(state.settings.notificationVolume / 100); } if (state.settings.showNotifications) { @@ -86,7 +87,9 @@ export const raiseNativeNotification = (notifications: Notification[]) => { return nativeNotification; }; -export const raiseSoundNotification = () => { +export const raiseSoundNotification = ( + volume = defaultSettings.notificationVolume / 100, +) => { const audio = new Audio( path.join( __dirname, @@ -96,6 +99,6 @@ export const raiseSoundNotification = () => { Constants.NOTIFICATION_SOUND, ), ); - audio.volume = 0.2; + audio.volume = volume; audio.play(); };