Skip to content

Commit b66a43e

Browse files
committed
feat: add stopwatch mode
1 parent 1223433 commit b66a43e

14 files changed

+149
-111
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ Available online at [https://kungfux.github.io/binarytimer/](https://kungfux.git
88

99
## Features
1010

11-
- 12-bit binary countdown timer
11+
- 12-bit binary timer
12+
- 2 modes: countdown and stopwatch
1213
- Maximum time is 4095 seconds (68 minutes 15 seconds)
1314
- Presets for 1, 2, 3, 5, 10, 15, 30, 60 minutes
1415
- Controls to prolong countdown time

src/App.tsx

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,33 @@ import routes from "./routes";
44
import TimerSetup from "./pages/TimerSetup.page";
55
import TimerCountdown from "./pages/TimerCountdown.page";
66
import Footer from "./components/Footer.component";
7+
import { OptionsProvider } from "./contexts/Options.context";
78
import { MuteButton } from "./components/MuteButton.component";
8-
import { MuteProvider } from "./contexts/Mute.context";
9-
import { HideProvider } from "./contexts/Hide.context";
109
import { HideButton } from "./components/HideButton.component";
10+
import { ModeButton } from "./components/ModeButton.component";
1111

1212
function App() {
1313
const location = useLocation();
1414
const queryParams = new URLSearchParams(location.search);
1515
const timeParam = queryParams.get("time");
1616

1717
return (
18-
<HideProvider>
19-
<MuteProvider>
20-
<div className="flex flex-col items-center justify-between min-h-screen h-screen min-w-96">
21-
<div className="flex justify-end w-full p-4">
22-
<HideButton />
23-
<MuteButton />
24-
</div>
25-
<Routes>
26-
<Route
27-
path={routes.root.path}
28-
element={timeParam ? <TimerCountdown /> : <TimerSetup />}
29-
/>
30-
</Routes>
31-
<Footer />
18+
<OptionsProvider>
19+
<div className="flex flex-col items-center justify-between min-h-screen h-screen min-w-96">
20+
<div className="flex justify-end w-full p-4">
21+
<ModeButton />
22+
<HideButton />
23+
<MuteButton />
3224
</div>
33-
</MuteProvider>
34-
</HideProvider>
25+
<Routes>
26+
<Route
27+
path={routes.root.path}
28+
element={timeParam ? <TimerCountdown /> : <TimerSetup />}
29+
/>
30+
</Routes>
31+
<Footer />
32+
</div>
33+
</OptionsProvider>
3534
);
3635
}
3736

src/BitCounter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@ class BitCounter {
4444
return this.getBits();
4545
}
4646

47-
public toString(): string {
47+
public toString(isStopwatchMode: boolean): string {
4848
const time = this.getTime();
4949

5050
if (time === 0) {
51-
return "Time's up!";
51+
return isStopwatchMode ? "0 seconds" : "Time's up!";
5252
}
5353

5454
const hours = Math.floor(time / 3600);

src/components/HideButton.component.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { useEffect } from "react";
22
import { faEye, faEyeSlash } from "@fortawesome/free-solid-svg-icons";
3-
import { useHideContext } from "../hooks/useHideContext.hook";
3+
import { useOptionsContext } from "../hooks/useOptionsContext.hook";
44
import { IconButton } from "./IconButton.component";
55

66
const HideButton = () => {
77
const IsHiddenSettingKey = "hide";
8-
const { isHidden, toggleHide } = useHideContext();
8+
const { isHidden, toggleHide } = useOptionsContext();
99

1010
useEffect(() => {
1111
const isHiddenSettingValue = localStorage.getItem(IsHiddenSettingKey);
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { useEffect } from "react";
2+
import { useOptionsContext } from "../hooks/useOptionsContext.hook";
3+
import { IconButton } from "./IconButton.component";
4+
import { faStopwatch, faStopwatch20 } from "@fortawesome/free-solid-svg-icons";
5+
6+
const ModeButton = () => {
7+
const IsStopwatchSettingKey = "stopwatch";
8+
const { isStopwatchMode, toggleStopwatchMode } = useOptionsContext();
9+
10+
useEffect(() => {
11+
const isStopwatchSettingValue = localStorage.getItem(IsStopwatchSettingKey);
12+
if (isStopwatchSettingValue && isStopwatchSettingValue !== isStopwatchMode.toString()) {
13+
toggleStopwatchMode();
14+
}
15+
// eslint-disable-next-line react-hooks/exhaustive-deps
16+
}, []);
17+
18+
useEffect(() => {
19+
localStorage.setItem(IsStopwatchSettingKey, isStopwatchMode.toString());
20+
}, [isStopwatchMode]);
21+
22+
return (
23+
<IconButton
24+
icon={isStopwatchMode ? faStopwatch : faStopwatch20}
25+
tooltip={isStopwatchMode ? "Switch to countdown mode" : "Switch to stopwatch mode"}
26+
onClick={() => toggleStopwatchMode()}
27+
/>
28+
);
29+
};
30+
31+
export { ModeButton };

src/components/MuteButton.component.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { useEffect } from "react";
22
import { faVolumeHigh, faVolumeXmark } from "@fortawesome/free-solid-svg-icons";
3-
import { useMuteContext } from "../hooks/useMuteContext.hook";
3+
import { useOptionsContext } from "../hooks/useOptionsContext.hook";
44
import { IconButton } from "./IconButton.component";
55

66
const MuteButton = () => {
77
const IsMutedSettingKey = "mute";
8-
const { isMuted, toggleMute } = useMuteContext();
8+
const { isMuted, toggleMute } = useOptionsContext();
99

1010
useEffect(() => {
1111
const isMutedSettingValue = localStorage.getItem(IsMutedSettingKey);

src/contexts/Hide.context.tsx

Lines changed: 0 additions & 22 deletions
This file was deleted.

src/contexts/Mute.context.tsx

Lines changed: 0 additions & 22 deletions
This file was deleted.

src/contexts/Options.context.tsx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { createContext, useState } from "react";
2+
3+
const Options = {
4+
isMuted: false,
5+
toggleMute: () => {},
6+
isHidden: false,
7+
toggleHide: () => {},
8+
isStopwatchMode: false,
9+
toggleStopwatchMode: () => {},
10+
};
11+
12+
const OptionsContext = createContext(Options);
13+
14+
const OptionsProvider = ({ children }: { children: React.ReactNode }) => {
15+
const [isMuted, setIsMuted] = useState(false);
16+
const [isHidden, setIsHidden] = useState(false);
17+
const [isStopwatchMode, setIsStopwatchMode] = useState(false);
18+
19+
const toggleMute = () => {
20+
setIsMuted(!isMuted);
21+
};
22+
23+
const toggleHide = () => {
24+
setIsHidden(!isHidden);
25+
};
26+
27+
const toggleStopwatchMode = () => {
28+
setIsStopwatchMode(!isStopwatchMode);
29+
};
30+
31+
return (
32+
<OptionsContext.Provider
33+
value={{
34+
isMuted,
35+
toggleMute,
36+
isHidden,
37+
toggleHide,
38+
isStopwatchMode,
39+
toggleStopwatchMode,
40+
}}
41+
>
42+
{children}
43+
</OptionsContext.Provider>
44+
);
45+
};
46+
47+
export { OptionsContext, OptionsProvider };

src/hooks/useHideContext.hook.tsx

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)