Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | 17x 39x 39x 59x 39x 35x 35x 2x 2x 2x 2x 33x 12x 12x 9x 9x 28x 28x 28x 28x 84x 22x 22x 22x 22x 21x 21x 28x 21x 21x 21x 21x 4x 17x | import type { MutableRefObject, Dispatch, SetStateAction } from 'react'
import type { Timer } from './useTimers'
/**
* Creates a timer tick function that decrements the remaining time
* and handles completion. Used by both addTimer and startTimer.
*/
export function createTimerTick(
id: string,
intervalsRef: MutableRefObject<Map<string, number>>,
onComplete: MutableRefObject<((timer: Timer) => void) | undefined>,
setTimers: Dispatch<SetStateAction<Timer[]>>
): () => void {
return () => {
setTimers((prev) =>
prev.map((timer) => {
if (timer.id !== id) return timer
if (!timer.isRunning) return timer
const newRemaining = timer.remaining - 1
if (newRemaining <= 0) {
clearInterval(intervalsRef.current.get(id))
intervalsRef.current.delete(id)
onComplete.current?.({ ...timer, remaining: 0, isRunning: false })
return { ...timer, remaining: 0, isRunning: false }
}
return { ...timer, remaining: newRemaining }
})
)
}
}
/**
* Clears an interval for a timer and removes it from the map.
*/
export function clearTimerInterval(
id: string,
intervalsRef: MutableRefObject<Map<string, number>>
): void {
const intervalId = intervalsRef.current.get(id)
if (intervalId) {
clearInterval(intervalId)
intervalsRef.current.delete(id)
}
}
/**
* Detects time mentions in text and returns durations in seconds.
*
* Supports patterns like:
* - "15 minutes", "15 min", "15m"
* - "2 hours", "2 hr", "2h"
* - "30 seconds", "30 sec", "30s"
*/
export function detectTimes(text: string): number[] {
const times: number[] = []
const seen = new Set<string>()
const patterns = [
{ regex: /(\d+)\s*(?:hours?|hrs?|h)\b/gi, multiplier: 3600 },
{ regex: /(\d+)\s*(?:minutes?|mins?|m)\b/gi, multiplier: 60 },
{ regex: /(\d+)\s*(?:seconds?|secs?|s)\b/gi, multiplier: 1 },
]
for (const { regex, multiplier } of patterns) {
let match
while ((match = regex.exec(text)) !== null) {
const value = parseInt(match[1], 10)
const seconds = value * multiplier
const key = `${match.index}-${value}-${multiplier}`
if (!seen.has(key) && seconds > 0) {
seen.add(key)
times.push(seconds)
}
}
}
return times
}
/**
* Formats seconds as a human-readable time string.
* e.g., 90 -> "1:30", 3661 -> "1:01:01"
*/
export function formatTimerDisplay(seconds: number): string {
const hrs = Math.floor(seconds / 3600)
const mins = Math.floor((seconds % 3600) / 60)
const secs = seconds % 60
if (hrs > 0) {
return `${hrs}:${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`
}
return `${mins}:${secs.toString().padStart(2, '0')}`
}
|