Skip to content

Commit

Permalink
feat: Added timer functionality (start/stop/reset)
Browse files Browse the repository at this point in the history
  • Loading branch information
Marpfie committed Jan 17, 2025
1 parent 27be5a5 commit fa00b6c
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 8 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@types/react-dom": "^18.3.5",
"@vitejs/plugin-react": "^4.3.4",
"autoprefixer": "^10.4.20",
"dayjs": "^1.11.13",
"eslint": "^9.17.0",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.16",
Expand Down
18 changes: 18 additions & 0 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import dayjs from "dayjs"

/**
* @param timestamp1 ISO string
* @param timestamp2 ISO string
* @returns Difference in milliseconds
*/
export const diffBetweenTimestamps = (
timestamp1: string,
timestamp2: string
): number => {
// Of course this isn't exactly a complicated function. It's put into this file just to demonstrate
// the use of library functions that may be reused across the application.
const date1 = dayjs(timestamp1)
const date2 = dayjs(timestamp2)

return date1.diff(date2)
}
5 changes: 5 additions & 0 deletions src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { StrictMode } from "react"
import { createRoot } from "react-dom/client"
import dayjs from "dayjs"
import duration from "dayjs/plugin/duration"

import { App } from "./App.tsx"
import "./index.scss"

// Add the duration plugin to dayjs
dayjs.extend(duration)

createRoot(document.getElementById("root")!).render(
<StrictMode>
<App />
Expand Down
26 changes: 22 additions & 4 deletions src/routes/timer/components/timerDisplay.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
import { useState } from "react"
import dayjs from "dayjs"
import { useMemo } from "react"

export const TimerDisplay = () => {
const [time] = useState(0)
interface ITimerDisplayProps {
elapsedTime: number
}

export const TimerDisplay = (props: ITimerDisplayProps) => {
const { elapsedTime } = props

const formattedTime = useMemo(() => {
const duration = dayjs.duration(elapsedTime)

if (duration.hours()) {
//TODO Check requirements for hour display
// This depends on the requirements. The `MM:ss` format has been defined,
// but not what should happen with the edge case of reaching an hour or more
return duration.format("HH:MM:ss")
}

return duration.format("MM:ss")
}, [elapsedTime])

return <div className="flex justify-center text-9xl m-6">{time}</div>
return <div className="flex justify-center text-9xl m-6">{formattedTime}</div>
}
58 changes: 54 additions & 4 deletions src/routes/timer/timer.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,65 @@
import { useCallback, useEffect, useRef, useState } from "react"
import dayjs from "dayjs"

import { Button } from "../../components/button"
import { diffBetweenTimestamps } from "../../lib/utils"

import { TimerDisplay } from "./components/timerDisplay"

export const Timer = () => {
const [elapsedTime, setElapsedTime] = useState(0) // Elapsed time in milliseconds
const [timestamp, setTimestamp] = useState("") // Timestamp of timer start/unpause
const [timerActive, setTimerActive] = useState(false)

const interval = useRef<number>(undefined)

const addElapsedTime = useCallback(() => {
// Adds elapsed time since last timestamp to the elapsedTime counter
setElapsedTime(
elapsedTime + diffBetweenTimestamps(dayjs().toISOString(), timestamp)
)
}, [elapsedTime, timestamp])

useEffect(() => {
if (timerActive) {
interval.current = setInterval(() => {
// Updates the elapsed time every 100 milliseconds.
// We do this instead of just a 1 second interval so the timer remains accurate after pausing
addElapsedTime()
setTimestamp(dayjs().toISOString())
}, 100)
}

return () => clearInterval(interval.current)
}, [addElapsedTime, elapsedTime, timerActive, timestamp])

const startTimer = () => {
setTimestamp(dayjs().toISOString())
setTimerActive(true)
}

const stopTimer = () => {
setTimerActive(false)
addElapsedTime()
}

const toggleTimer = () => (timerActive ? stopTimer() : startTimer())

const resetTimer = () => {
setTimerActive(false)
setElapsedTime(0)
}

return (
<>
<h1 className="flex justify-center text-5xl m-6">Trial Timer</h1>
<TimerDisplay />
<h1>{timestamp}</h1>
<h1>{elapsedTime}</h1>
<TimerDisplay elapsedTime={elapsedTime} />
<div className="grid grid-cols-3 gap-3">
<Button>Start</Button>
<Button>Reset</Button>
<Button>Lap</Button>
<Button onClick={toggleTimer}>{timerActive ? "Stop" : "Start"}</Button>
<Button onClick={resetTimer}>Reset</Button>
<Button>Lap - Todo :)</Button>
</div>
</>
)
Expand Down
7 changes: 7 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -918,6 +918,11 @@ csstype@^3.0.2:
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==

dayjs@^1.11.13:
version "1.11.13"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c"
integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==

debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
version "4.4.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a"
Expand Down Expand Up @@ -1927,6 +1932,7 @@ source-map-js@^1.2.1:
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==

"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0:
name string-width-cjs
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
Expand All @@ -1945,6 +1951,7 @@ string-width@^5.0.1, string-width@^5.1.2:
strip-ansi "^7.0.1"

"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
name strip-ansi-cjs
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
Expand Down

0 comments on commit fa00b6c

Please sign in to comment.