Skip to content

feat: simple custom sound selection #606

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/electron/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type StoreProps = {
* This also ensures that we can force calling the store safely. Though I have switched the names to safeGet and safeSet to make it more clear.
*/
class SafeStore<
T extends Record<string, any> = Record<string, unknown>,
T extends Record<string, any> = Record<string, unknown>
> {
private store: ElectronStore<T>;
constructor(props: Options<T>) {
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
38 changes: 38 additions & 0 deletions app/renderer/src/components/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { SVG } from "components";
import React, { useState } from "react";
import {
StyledDropdown,
StyledDropdownContent,
StyledDropdownHeading,
} from "styles";

type Props = {
children?: React.ReactNode;
};

// Todo, convert this into a dropdown menu instead of Radio buttons
const Dropdown: React.FC<Props> = ({ children }) => {
const [open, setOpen] = useState(false);

const toggleDropdown = () => {
setOpen((prevState) => !prevState);
};

return (
<StyledDropdown>
<StyledDropdownHeading
as={"button"}
open={open}
onClick={toggleDropdown}
>
Notification Sound
<SVG name="chevron-down" />
</StyledDropdownHeading>
{open && (
<StyledDropdownContent>{children}</StyledDropdownContent>
)}
</StyledDropdown>
);
};

export default React.memo(Dropdown);
2 changes: 2 additions & 0 deletions app/renderer/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export { default as Radio } from "./Radio";

export { default as Collapse } from "./Collapse";

export { default as Dropdown } from "./Dropdown";

export { default as Alert } from "./Alert";
export { default as Updater } from "./Updater";
export { default as NavNotify } from "./NavNotify";
Expand Down
1 change: 1 addition & 0 deletions app/renderer/src/contexts/CounterContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const CounterProvider: React.FC = ({ children }) => {
{
icon: notificationIcon,
mute: !settings.notificationSoundOn,
notificationSound: settings.notificationSound,
},
settings.notificationType !== "none"
);
Expand Down
20 changes: 19 additions & 1 deletion app/renderer/src/hooks/useNotification.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import bell from "assets/audios/notification-bell.wav";
import pomodoro from "assets/audios/notification/pomodoro.mp3";
import treasure from "assets/audios/notification/treasure.mp3";
import trumpets from "assets/audios/notification/trumpets.mp3";
import { NotificationSounds } from "store/settings/types";

type OptionProps = {
mute?: boolean;
notificationSound: NotificationSounds;
} & NotificationOptions;

export const useNotification = (
Expand All @@ -23,7 +28,20 @@ export const useNotification = (
// in all Operating System

if (!constantOptions?.mute) {
new Audio(bell).play().catch((e) => {
let sound;

switch (constantOptions?.notificationSound) {
case NotificationSounds.POMODORO:
sound = pomodoro;
break;
case NotificationSounds.TRUMPETS:
sound = trumpets;
break;
default:
sound = bell;
}

new Audio(sound).play().catch((e) => {
console.warn("There was a problem playing sound", e);
});

Expand Down
55 changes: 53 additions & 2 deletions app/renderer/src/routes/Settings/FeatureSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
setEnableStrictMode,
setEnableProgressAnimation,
setNotificationType,
setNotificationSound,
setEnableFullscreenBreak,
setUseNativeTitlebar,
setAutoStartWorkTime,
Expand All @@ -14,12 +15,21 @@ import {
setEnableCompactMode,
setOpenAtLogin,
} from "store";
import { Toggler, TogglerProps, Collapse, Radio } from "components";
import {
Toggler,
TogglerProps,
Collapse,
Radio,
Dropdown,
} from "components";
import { ThemeContext } from "contexts";

import SettingSection from "./SettingSection";
import { detectOS } from "utils";
import { NotificationTypes } from "store/settings/types";
import {
NotificationTypes,
NotificationSounds,
} from "store/settings/types";

const FeatureSection: React.FC = () => {
const settings = useAppSelector((state) => state.settings);
Expand Down Expand Up @@ -149,6 +159,15 @@ const FeatureSection: React.FC = () => {
[dispatch]
);

const onChangeNotificationSound = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
dispatch(
setNotificationSound(e.target.value as NotificationSounds)
);
},
[dispatch]
);

return (
<SettingSection heading="App Features">
{featureList.map(
Expand Down Expand Up @@ -193,6 +212,38 @@ const FeatureSection: React.FC = () => {
onChange={onChangeNotificationProps}
/>
</Collapse>
<Dropdown>
<Radio
id="default"
label="default"
name="notification-sound"
value={NotificationSounds.DEFAULT}
checked={
settings.notificationSound === NotificationSounds.DEFAULT
}
onChange={onChangeNotificationSound}
/>
<Radio
id="pomodoro"
label="pomodoro"
name="notification-sound"
value={NotificationSounds.POMODORO}
checked={
settings.notificationSound === NotificationSounds.POMODORO
}
onChange={onChangeNotificationSound}
/>
<Radio
id="trumpets"
label="trumpets"
name="notification-sound"
value={NotificationSounds.TRUMPETS}
checked={
settings.notificationSound === NotificationSounds.TRUMPETS
}
onChange={onChangeNotificationSound}
/>
</Dropdown>
</SettingSection>
);
};
Expand Down
7 changes: 6 additions & 1 deletion app/renderer/src/store/settings/defaultSettings.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { NotificationTypes, SettingTypes } from "./types";
import {
NotificationTypes,
NotificationSounds,
SettingTypes,
} from "./types";
import { detectOS, isPreferredDark } from "utils";

export const defaultSettings: Readonly<SettingTypes> = Object.freeze({
Expand All @@ -12,6 +16,7 @@ export const defaultSettings: Readonly<SettingTypes> = Object.freeze({
enableVoiceAssistance: false,
notificationSoundOn: true,
notificationType: NotificationTypes.NONE,
notificationSound: NotificationSounds.DEFAULT,
closeToTray: true,
minimizeToTray: false,
autoStartWorkTime: false,
Expand Down
8 changes: 8 additions & 0 deletions app/renderer/src/store/settings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ const settingsSlice = createSlice({
state.notificationType = action.payload;
},

setNotificationSound(
state,
action: SettingsPayload<"notificationSound">
) {
state.notificationSound = action.payload;
},

setCloseToTray(state, action: SettingsPayload<"closeToTray">) {
state.closeToTray = action.payload;
},
Expand Down Expand Up @@ -125,6 +132,7 @@ export const {
setIgnoreUpdate,
setMinimizeToTray,
setNotificationType,
setNotificationSound,
setOpenAtLogin,
setUseNativeTitlebar,
toggleNotificationSound,
Expand Down
8 changes: 8 additions & 0 deletions app/renderer/src/store/settings/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,17 @@ export type SettingTypes = {
minimizeToTray: boolean;
autoStartWorkTime: boolean;
notificationType: NotificationTypes;
notificationSound: NotificationSounds;
openAtLogin: boolean;
};

export const enum NotificationSounds {
DEFAULT = "default", // Windows sound....
TREASURE = "treasure", // https://pixabay.com/sound-effects/short-success-sound-glockenspiel-treasure-video-game-6346/
TRUMPETS = "trumpets", //https://pixabay.com/sound-effects/success-fanfare-trumpets-6185/
POMODORO = "pomodoro", // https://pixabay.com/sound-effects/tomato-squishwet-103934/
}

export const enum NotificationTypes {
NONE = "none",
NORMAL = "normal",
Expand Down
90 changes: 90 additions & 0 deletions app/renderer/src/styles/components/dropdown.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import styled from "styled-components/macro";

export const StyledDropdown = styled.div`
width: 100%;
min-height: 4rem;

display: grid;
grid-template-rows: 4rem;
`;

export const StyledDropdownHeading = styled.h4<{ open?: boolean }>`
width: 100%;
height: 100%;

display: flex;
align-items: center;
justify-content: space-between;

color: var(--color-body-text);
font-weight: 400;
border: none;
background-color: transparent;

&:focus {
color: var(--color-primary);
}

& > svg {
width: 1.4rem;
height: 1.4rem;

margin-right: 0.8rem;

transform: ${(p) => p.open && "rotate(180deg)"};
transition: transform 180ms ease;
}

position: relative;

&::after {
content: "";
position: absolute;
bottom: 0;
left: 0;

width: 100%;
height: 1px;

background-color: var(--color-border-secondary);
}
`;

export const StyledDropdownContent = styled.div`
width: 100%;
height: 4rem;

display: flex;
align-items: center;

animation: enter 180ms ease;

& > label:not(:last-of-type) {
margin-right: 3.2rem;
}

position: relative;

&::after {
content: "";
position: absolute;
bottom: 0;
left: 0;

width: 100%;
height: 1px;

background-color: var(--color-border-secondary);
}

@keyframes enter {
0% {
opacity: 0;
transform: translateY(-1rem);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
`;
1 change: 1 addition & 0 deletions app/renderer/src/styles/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ export * from "./popper";
export * from "./loaders";
export * from "./collapse";
export * from "./alert";
export * from "./dropdown";
1 change: 1 addition & 0 deletions app/renderer/src/typings.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ declare module "*.woff2";
declare module "*.png";
declare module "*.jpg";
declare module "*.wav";
declare module "*.mp3";
declare module "*.mp4";
declare module "*.ogv";
declare module "*.webm";