Skip to content

Commit

Permalink
add volume controls for notification sounds
Browse files Browse the repository at this point in the history
  • Loading branch information
marinoffDev committed Oct 16, 2024
1 parent 28952a0 commit 81fe78a
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 14 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Future plans for enhancements and development:
- [x] Add browser cookies support to store the user's customization preferences
- [x] Add an option to start subsequent timers automatically
- [x] Add a component that provides user feedback when their custom preferences have been applied
- [ ] Add a customization option for the volume of notification sounds
- [x] Add volume controls for the notification sound
- [ ] Add more theme customization options
- [ ] Add a toggle for "super dark mode" while a timer is active
- [ ] Add SSG build step before deployment
Expand Down
36 changes: 31 additions & 5 deletions src/components/Customize.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { faGear } from "@fortawesome/free-solid-svg-icons";
import { useToast } from "@/components/hooks/use-toast"
import { defaultSettings } from "@/lib/defaultSettings"
import { notificationSounds } from "@/lib/notificationSounds"
import TimerSlider from "@/components/ui/TimerSlider";
import CustomSlider from "@/components/ui/CustomSlider";

export default function Customize({ timerSettings, onSaveTimerSettings }) {
const { toast } = useToast();
Expand All @@ -39,6 +39,7 @@ export default function Customize({ timerSettings, onSaveTimerSettings }) {
longBreak: timerSettings.longBreak / 60,
sessionRounds: timerSettings.sessionRounds,
notificationSound: timerSettings.notificationSound,
volumeLevel: timerSettings.volumeLevel,
autoStartPomodoro : timerSettings.autoStartPomodoro,
autoStartBreak : timerSettings.autoStartBreak
}));
Expand All @@ -49,10 +50,19 @@ export default function Customize({ timerSettings, onSaveTimerSettings }) {
setSettings((prevSettings) => ({ ...prevSettings, [type]: value }));
};

// Handle notification sound change and play preview sound
const handleVolumeChange = (value) => {
setSettings((prevSettings) => ({ ...prevSettings, volumeLevel: value }));
const sound = new Audio(settings.notificationSound);
sound.volume = (value / 100);
sound.play();
};

// Handle notification sound change and play preview sound
const handleSoundChange = (value) => {
setSettings((prevSettings) => ({ ...prevSettings, notificationSound: value }));
const sound = new Audio(value);
sound.volume = settings.volumeLevel / 100
sound.play();
};

Expand All @@ -64,6 +74,7 @@ export default function Customize({ timerSettings, onSaveTimerSettings }) {
longBreak: defaultSettings.longBreak / 60,
sessionRounds: defaultSettings.sessionRounds,
notificationSound: defaultSettings.notificationSound,
volumeLevel: defaultSettings.volumeLevel,
autoStartPomodoro : defaultSettings.autoStartPomodoro,
autoStartBreak : defaultSettings.autoStartBreak
});
Expand All @@ -77,6 +88,7 @@ export default function Customize({ timerSettings, onSaveTimerSettings }) {
longBreak: settings.longBreak * 60,
sessionRounds: settings.sessionRounds,
notificationSound: settings.notificationSound,
volumeLevel: settings.volumeLevel,
autoStartPomodoro : settings.autoStartPomodoro,
autoStartBreak : settings.autoStartBreak
});
Expand All @@ -101,36 +113,40 @@ export default function Customize({ timerSettings, onSaveTimerSettings }) {
</DialogHeader>
<DialogDescription asChild>
<div>
<TimerSlider
<CustomSlider
label="Pomodoro"
unit="minute"
value={settings.pomodoro}
min={1}
max={60}
isTimerControls={true}
onChange={(value) => handleChange("pomodoro", value)}
/>
<TimerSlider
<CustomSlider
label="Short Break"
unit="minute"
value={settings.shortBreak}
min={1}
max={30}
isTimerControls={true}
onChange={(value) => handleChange("shortBreak", value)}
/>
<TimerSlider
<CustomSlider
label="Long Break"
unit="minute"
value={settings.longBreak}
min={1}
max={60}
isTimerControls={true}
onChange={(value) => handleChange("longBreak", value)}
/>
<TimerSlider
<CustomSlider
label="Session Rounds"
unit="round"
value={settings.sessionRounds}
min={1}
max={10}
isTimerControls={true}
onChange={(value) => handleChange("sessionRounds", value)}
/>
<div className="mt-5 flex items-center justify-between">
Expand All @@ -154,6 +170,16 @@ export default function Customize({ timerSettings, onSaveTimerSettings }) {
</SelectContent>
</Select>
</div>
<CustomSlider
label="Notification Volume"
unit="%"
value={settings.volumeLevel}
min={0}
max={100}
step={25}
isTimerControls={false}
onChange={handleVolumeChange}
/>
</div>
</DialogDescription>
<DialogFooter className="mt-4 flex justify-center gap-2">
Expand Down
10 changes: 5 additions & 5 deletions src/components/Timer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,17 @@ import { useState, useEffect, useRef, useCallback } from "react";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import TimerCard from "@/components/TimerCard.jsx";
import { formatTime } from "@/lib/utils";
import { notificationSounds } from "@/lib/notificationSounds"

export default function Timer({ settings }) {
const { pomodoro, shortBreak, longBreak, sessionRounds, notificationSound, autoStartPomodoro, autoStartBreak } = settings;
const { pomodoro, shortBreak, longBreak, sessionRounds, notificationSound, volumeLevel, autoStartPomodoro, autoStartBreak } = settings;
const [activeTimer, setActiveTimer] = useState("pomodoro");
const [time, setTime] = useState(pomodoro);
const [remainingTime, setRemainingTime] = useState(pomodoro);
const [timerActive, setTimerActive] = useState(false);
const [pomodoroRounds, setpomodoroRounds] = useState(0);
const audioRef = useRef(new Audio(notificationSound));
const intervalRef = useRef(null);
const startTimeRef = useRef(null);
const audioRef = useRef(new Audio(notificationSound || notificationSounds["Mission Accomplished"]));

const startTimer = useCallback(() => {
setTimerActive(true);
Expand Down Expand Up @@ -74,6 +73,7 @@ export default function Timer({ settings }) {

useEffect(() => {
if (timerActive) {
requestNotificationPermission();
startTimeRef.current = Date.now();
intervalRef.current = setInterval(() => {
const elapsedTime = Math.floor((Date.now() - startTimeRef.current) / 1000);
Expand All @@ -95,7 +95,6 @@ export default function Timer({ settings }) {
useEffect(() => {
if (timerActive) {
document.title = `${formatTime(time)} - Focus Fox`;
requestNotificationPermission();
} else {
document.title = "Focus Fox";
}
Expand Down Expand Up @@ -143,7 +142,8 @@ export default function Timer({ settings }) {

useEffect(() => {
audioRef.current = new Audio(notificationSound);
}, [notificationSound]);
audioRef.current.volume = volumeLevel / 100
}, [notificationSound, volumeLevel]);

const requestNotificationPermission = () => {
if (Notification.permission !== "granted") {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import { Label } from "@/components/ui/label";
import { Slider } from "@/components/ui/slider";

export default function TimerSlider({ label, unit, value, min, max, onChange }) {
export default function CustomSlider({ label, unit, value, min, max, onChange, isTimerControls, step }) {
return (
<>
<div className="flex justify-between my-4">
<Label>{label}</Label>
<Label>{`${value} ${value > 1 ? unit + 's' : unit}`}</Label>
{isTimerControls && <Label>{`${value} ${value > 1 ? unit + 's' : unit}`}</Label>}
{!isTimerControls && <Label>{`${value} ${unit}`}</Label>}
</div>
<Slider
value={[value]} // React Slider expects an array for a single value
min={min}
max={max}
step={1}
step={step || 1}
onValueChange={(newValue) => onChange(newValue[0])} // Pass new value back to parent
/>
</>
Expand Down
1 change: 1 addition & 0 deletions src/lib/defaultSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const defaultSettings = {
longBreak: 15 * 60,
sessionRounds: 4,
notificationSound: notificationSounds["Mission Accomplished"],
volumeLevel: 50,
autoStartPomodoro : false,
autoStartBreak : false
}
1 change: 1 addition & 0 deletions src/routes/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ function App() {
longBreak: Number(parsedSettings.longBreak),
sessionRounds: Number(parsedSettings.sessionRounds),
notificationSound: parsedSettings.notificationSound,
volumeLevel: Number(parsedSettings.volumeLevel),
autoStartPomodoro: parsedSettings.autoStartPomodoro === true,
autoStartBreak: parsedSettings.autoStartBreak === true,
}));
Expand Down

0 comments on commit 81fe78a

Please sign in to comment.