Introduction
I was trying to search my some free tool that can these 2 things.
- Run the Timer with Given minutes
- Keep Screen WakeUp when timer is
This time helps me to stay focus and time-bound any task which I want perform. For ex - I want to write this article in 1 hour then it helps me to stay focused during that 1 hour and remaining time helps me to stay motivated.
I wasn't able to find any such tool which free. There are many tools which have timer like Pomodoro etc. But for wake-up screen feature come with paid subscription. I don't need any analytics like how time for any particular day I was focused like that.
So, I decided to build it by myself in React and use wake-up screen api of browser to leverage the wake-up functionality.
This is deployed here - we can use it free of cost.
https://react-app-ankit.web.app/timer
Sample Code
Here is the sample Code for react with Typescript
import React, { useState, useEffect, useRef } from "react";
import { useWakeLock } from "react-screen-wake-lock";
import { Container, Typography, TextField, Button, Box } from "@mui/material";
const TimerWithWakeLock: React.FC = () => {
const [inputMinutes, setInputMinutes] = useState<string>("10");
const [remainingSeconds, setRemainingSeconds] = useState<number | null>(null);
const [running, setRunning] = useState<string | null>(null);
const intervalRef = useRef<NodeJS.Timeout | null>(null);
const { isSupported, released, request: requestWakeCall, release: releaseWakeCall } = useWakeLock();
const startTimer = () => {
const totalSeconds = parseInt(inputMinutes) * 60;
if (isNaN(totalSeconds) || totalSeconds <= 0) return;
setRemainingSeconds(totalSeconds);
setRunning("started");
requestWakeCall();
};
const stopTimer = () => {
setRunning(null);
setRemainingSeconds(null);
if (intervalRef.current) clearInterval(intervalRef.current);
releaseWakeCall();
};
const resumeTimer = () => {
// setRemainingSeconds(remainingSeconds);
setRunning("resumed");
requestWakeCall();
};
const pauseTimer = () => {
setRunning("paused");
if (intervalRef.current) clearInterval(intervalRef.current);
releaseWakeCall();
};
useEffect(() => {
if ( (running === "started" || running === "resumed") && remainingSeconds !== null) {
intervalRef.current = setInterval(() => {
setRemainingSeconds((prev) => {
if (prev !== null && prev > 0) {
return prev - 1;
} else {
const audio = new Audio("/beep2.mp3");
audio.play();
stopTimer();
return 0;
}
});
}, 1000);
}
return () => {
if (intervalRef.current) clearInterval(intervalRef.current);
};
}, [running]);
const formatTime = (totalSeconds: number) => {
const minutes = Math.floor(totalSeconds / 60)
.toString()
.padStart(2, "0");
const seconds = (totalSeconds % 60).toString().padStart(2, "0");
return `${minutes}:${seconds}`;
};
return (
<Container
className="min-h-screen flex flex-col items-center justify-center bg-gray-900 text-white p-4"
maxWidth="xs"
>
<Typography variant="h3" className="text-yellow-400">
⏰ Timer
</Typography>
{!isSupported && (
<Typography color="error" className="text-sm">
Wake Lock API not supported in this browser
</Typography>
)}
{!running && (
<Box sx={{ m: 3 }} className="flex flex-col items-center gap-2">
<TextField
type="number"
value={inputMinutes}
onChange={(e) => setInputMinutes(e.target.value)}
label="Enter minutes"
variant="outlined"
className="w-full"
aria-label="Input minutes for the timer"
/>
<Button
onClick={startTimer}
variant="contained"
color="primary"
className="bg-green-600 hover:bg-green-700"
aria-label="Start the timer"
>
Start
</Button>
</Box>
)}
{running && (
<Box sx={{ flex: "flex", flexDirection: "column", alignItems: "", gap: "4" }}>
<Typography variant="h1" className="text-6xl text-green-400">
{formatTime(remainingSeconds ?? 0)}
</Typography>
<Button
onClick={stopTimer}
variant="contained"
color="primary"
className="bg-red-600 hover:bg-red-700"
aria-label="Stop the timer"
>
Stop
</Button>
<Button
onClick={() => {
if (running !== "paused") {
pauseTimer();
} else if (remainingSeconds !== null) {
resumeTimer();
}
}}
variant="contained"
color="primary"
className="bg-red-600 hover:bg-red-700"
sx={{ m: 2 }}
aria-label={running === "paused" ? "Resume the timer" : "Pause the timer"}
>
{running === "paused" ? "Resume" : "Pause"}
</Button>
<Typography variant="body2" className="text-sm text-gray-400">
Wake Lock Active: {released ? "No" : "Yes"}
</Typography>
<Button
onClick={() => {
released ? requestWakeCall() : releaseWakeCall();
}}
variant="contained"
color="secondary"
className="bg-blue-600 hover:bg-blue-700"
aria-label={released ? "Request wake lock" : "Release wake lock"}
>
{released ? "Request Wake Lock" : "Release Wake Lock"}
</Button>
</Box>
)}
</Container>
);
};
export default TimerWithWakeLock;
Top comments (0)