All files / react-app/src/components/TimeoutModal index.tsx

95.45% Statements 21/22
100% Branches 6/6
83.33% Functions 5/6
95.45% Lines 21/22

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                                  71x 71x   71x 11x 11x         11x 11x   11x 2x 2x     11x 1x     1x         11x 3x 1x       11x 9x 4x 4x       11x                                                              
import { useGetUser } from "@/api";
import {
  Button,
  Dialog,
  DialogContent,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components";
import { useCountdown, useIdle } from "@/hooks";
import { DialogDescription } from "@radix-ui/react-dialog";
import { Auth } from "aws-amplify";
import { intervalToDuration } from "date-fns";
import pluralize from "pluralize";
import { useEffect, useState } from "react";
import { removeItemLocalStorage } from "@/hooks/useLocalStorage";
 
const TWENTY_MINS_IN_MILS = 1000 * 60 * 20;
const TEN_MINS_IN_MILS = 60 * 10;
 
export const TimeoutModal = () => {
  const [isModalOpen, setIsModalOpen] = useState(false);
  const isIdleForTwentyMins = useIdle(TWENTY_MINS_IN_MILS, {
    initialState: false,
  });
 
  const [timeoutModalCountdown, { startCountdown, resetCountdown }] =
    useCountdown(TEN_MINS_IN_MILS);
  const { data: user, isLoading: isUserLoading } = useGetUser();
 
  const onLogOut = () => {
    Auth.signOut();
    removeItemLocalStorage();
  };
 
  const onExtendSession = () => {
    setIsModalOpen(false);
 
    // artificial delay hiding the countdown reset after modal dismissal
    setTimeout(() => {
      resetCountdown();
    }, 500);
  };
 
  useEffect(() => {
    if (timeoutModalCountdown === 0) {
      onLogOut();
    }
  }, [timeoutModalCountdown]);
 
  useEffect(() => {
    if (user?.user && isIdleForTwentyMins) {
      startCountdown();
      setIsModalOpen(true);
    }
  }, [isIdleForTwentyMins, user, isUserLoading, startCountdown]);
 
  const duration = intervalToDuration({
    start: 0,
    end: timeoutModalCountdown * 1000,
  });
 
  return (
    <Dialog open={isModalOpen} onOpenChange={onExtendSession}>
      <DialogContent className="sm:max-w-[425px]">
        <DialogDescription className="sr-only">Session expiring soon</DialogDescription>
        <DialogHeader>
          <DialogTitle>Session expiring soon</DialogTitle>
        </DialogHeader>
        <div className="py-4">
          <span>
            Your session will expire in <strong>{duration.minutes}</strong>{" "}
            {pluralize("minute", duration.minutes)} and <strong>{duration.seconds}</strong>{" "}
            {pluralize("second", duration.seconds)}.
          </span>
        </div>
        <DialogFooter>
          <Button type="submit" onClick={onExtendSession}>
            Yes, extend session
          </Button>
          <Button type="button" variant="outline" onClick={onLogOut}>
            No, sign out
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
};