From 777bea93acdc8ce6b22bff47288b34ee81296e26 Mon Sep 17 00:00:00 2001 From: Dane Johnson Date: Sat, 25 Feb 2023 12:32:02 -0600 Subject: [PATCH] Decoupled timer and timer widget --- src/Timer.tsx | 48 ------------------------------------- src/TimerWidget.tsx | 21 ++++++++++++++++ src/display/ClueDisplay.tsx | 12 ++++++---- src/hooks.ts | 44 ++++++++++++++++++++++++++++++++++ src/host/ActiveClue.tsx | 8 +++++++ 5 files changed, 80 insertions(+), 53 deletions(-) delete mode 100644 src/Timer.tsx create mode 100644 src/TimerWidget.tsx diff --git a/src/Timer.tsx b/src/Timer.tsx deleted file mode 100644 index 8355408..0000000 --- a/src/Timer.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { useState, useEffect } from "react"; -import { clamp } from "lodash"; - -interface Props { - startTime: number | null; - length: number; - onTimeout?: () => unknown; -} - -const invLerp = (a: number, b: number, x: number): number => - clamp((x - b) / (a - b), 0, 1); - -const Timer = ({ startTime, length, onTimeout }: Props) => { - const [segs, setSegs] = useState(0); - const updateTimer = () => { - if (!startTime) return; - const endTime = startTime + length; - const currentTime = Date.now(); - const segs = invLerp(startTime, endTime, currentTime) * 5; - setSegs(segs); - if (segs > 0) { - setTimeout(updateTimer, 100); - } else if (onTimeout) { - onTimeout(); - } - }; - useEffect(() => { - setTimeout(updateTimer, 100); - }, [startTime, length]); - - return ( - - {[5, 4, 3, 2, 1, 2, 3, 4, 5].map((seg, i) => ( - - ))} - - ); -}; - -export default Timer; diff --git a/src/TimerWidget.tsx b/src/TimerWidget.tsx new file mode 100644 index 0000000..57287b4 --- /dev/null +++ b/src/TimerWidget.tsx @@ -0,0 +1,21 @@ +interface Props { + segs: number; +} + +const TimerWidget = ({ segs }: Props) => ( + + {[5, 4, 3, 2, 1, 2, 3, 4, 5].map((seg, i) => ( + + ))} + +); + +export default TimerWidget; diff --git a/src/display/ClueDisplay.tsx b/src/display/ClueDisplay.tsx index ba05e79..c13d16c 100644 --- a/src/display/ClueDisplay.tsx +++ b/src/display/ClueDisplay.tsx @@ -1,24 +1,26 @@ -import { useState, useEffect } from "react"; +import { useEffect } from "react"; -import Timer from "../Timer"; +import TimerWidget from "../TimerWidget"; import type { Clue } from "../store/cluesSlice"; import { socket } from "../socket"; +import { useTimer } from "../hooks"; + interface Props { activeClue: Clue; } const ClueDisplay = ({ activeClue }: Props) => { - const [startTime, setStartTime] = useState(null); + const { start, segs } = useTimer(5000); useEffect(() => { - socket.on("clue-clock-on", () => setStartTime(Date.now())); + socket.on("clue-clock-on", start); }, []); return ( <>
{activeClue.question}
- + ); }; diff --git a/src/hooks.ts b/src/hooks.ts index 8768ccf..f3c36df 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -1,5 +1,49 @@ +import { useState } from "react"; import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"; import type { RootState, AppDispatch } from "./store"; +import { clamp } from "lodash"; export const useAppDispatch: () => AppDispatch = useDispatch; export const useAppSelector: TypedUseSelectorHook = useSelector; + +const invLerp = (a: number, b: number, x: number): number => + clamp((x - b) / (a - b), 0, 1); + +interface Timer { + segs: number; + start: () => void; + cancel: () => void; +} + +export const useTimer = (length: number, onTimeout?: () => unknown): Timer => { + const [segs, setSegs] = useState(0); + const [timeoutId, setTimeoutId] = useState>(); + + const updateTimer = (startTime: number) => { + const endTime = startTime + length; + const currentTime = Date.now(); + const segs = invLerp(startTime, endTime, currentTime) * 5; + setSegs(segs); + if (segs > 0) { + setTimeoutId(setTimeout(updateTimer, 100, startTime)); + } else if (onTimeout) { + onTimeout(); + } + }; + + const start = () => { + const startTime = Date.now(); + setTimeoutId(setTimeout(updateTimer, 100, startTime)); + }; + + const cancel = () => { + clearTimeout(timeoutId); + setSegs(0); + }; + + return { + segs, + start, + cancel, + }; +}; diff --git a/src/host/ActiveClue.tsx b/src/host/ActiveClue.tsx index 5fd9085..d413048 100644 --- a/src/host/ActiveClue.tsx +++ b/src/host/ActiveClue.tsx @@ -35,6 +35,14 @@ const ActiveClue = ({ activeClue }: Props) => { {mode === "reading" && ( )} + {mode === "running" && ( // TODO remove this + + )} {mode === "buzzed" && ( <>