DEV Community

Ankit malik
Ankit malik

Posted on

Created my own timer with Wakeup Screen in React

Introduction

I was trying to search my some free tool that can these 2 things.

  1. Run the Timer with Given minutes
  2. 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;

Enter fullscreen mode Exit fullscreen mode

ScreenShots

Time when stopped

timer when running

Top comments (0)