orbisPlayground/src/components/SessionTimeout/index.jsx

164 lines
5.9 KiB
JavaScript

import React, { Fragment, useEffect, useRef, useState } from 'react'
import { Dialog, Transition } from '@headlessui/react'
import PropTypes from 'prop-types'
import useHasMounted from '@/hooks/useHasMounted'
export default function SessionTimeout ({ i18n, user, onTimeout, onCancelTimeout }) {
const TIME = 30
const hasMounted = useHasMounted()
const countTimerRef = useRef()
const [tick, setTick] = useState(0)
const [countTimer, setCountTimer] = useState() // contador que lleva el control de tiempo en segundos para mostrar antes de cerrar sesion
const [timerIsOn, setTimerIsOn] = useState(false) // verifica si el dialogo debe ser mostrado
const idleTimer = useRef(null) // variable de control para el proceso de conteo
// realiza el conteo regresivo
const timedCount = () => {
if (countTimerRef.current <= 0) {
clearInterval(idleTimer.current)
setTimerIsOn(false)
setCountTimer(TIME)
onTimeout()
return
}
setCountTimer(countTimerRef.current - 1)
}
// inicia el conteo regresivo
const startIdleMonitor = (expiracion = 0) => {
if (expiracion < TIME * 1000) {
countTimerRef.current = Math.round(expiracion / 1000) // tiempo restante
} else {
countTimerRef.current = TIME
}
setCountTimer(countTimerRef.current)
setTimerIsOn(true)
timedCount()
idleTimer.current = setInterval(timedCount, 1000)
}
const calendarizeIdle = () => {
// const fecha_inicio_sesion = new Date(user.fecha_inicio_sesion.slice(0, -1)) // uso de localtime quita Z al final del string
const fecha_fin_sesion = new Date(user?.fecha_fin_sesion?.slice(0, -1)) // uso de localtime quita Z al final del string
// const fecha_fin_sesion = new Date(user.fecha_fin_sesion)
const expiracion = Math.round(fecha_fin_sesion.getTime() - (new Date()).getTime()) // valida cuando se realiza un F5 o reload del navegador (user.tiempo_expiracion * 60 * 1000)
if (expiracion <= 0) {
return // no debe calendarizar porque la sesion ya vencio
}
const timeInMillis = expiracion - (TIME * 1000)
const processTimer = setTimeout(startIdleMonitor, timeInMillis > 0 ? timeInMillis : 0, expiracion)
return () => clearTimeout(processTimer)
}
// cancela el conteo regresivo aumentando el tiempo de expiracion
const stopIdleMonitor = async () => {
await onCancelTimeout()
clearInterval(idleTimer.current)
setTimerIsOn(false)
setCountTimer(TIME)
// force rerender to update view
setTick((tick) => tick + 1)
// await new Promise(resolve => setTimeout(resolve, 2000)) // espera obligada para actualizacion DB
}
useEffect(() => {
if (timerIsOn === false && tick > 0) {
return calendarizeIdle()
}
}, [timerIsOn, tick])
useEffect(() => {
countTimerRef.current = countTimer
}, [countTimer])
useEffect(() => {
if (!user || user === null) {
if (idleTimer && idleTimer.current) {
clearInterval(idleTimer.current)
}
}
return () => clearInterval(idleTimer.current)
}, [user])
useEffect(() => {
if (hasMounted) {
return calendarizeIdle()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [hasMounted])
return (
<>
{ timerIsOn && timerIsOn === true &&
<Transition appear show as={Fragment}>
<Dialog as='div' className='relative' style={{ zIndex: 999 }} onClose={() => { }}>
<Transition.Child
as={Fragment}
enter='ease-out duration-300'
enterFrom='opacity-0'
enterTo='opacity-100'
leave='ease-in duration-200'
leaveFrom='opacity-100'
leaveTo='opacity-0'
>
<div className='fixed inset-0 bg-black/25' />
</Transition.Child>
<div className='fixed inset-0 overflow-y-auto'>
<div className='flex min-h-full items-center justify-center p-4 text-center'>
<Transition.Child
as={Fragment}
enter='ease-out duration-300'
enterFrom='opacity-0 scale-95'
enterTo='opacity-100 scale-100'
leave='ease-in duration-200'
leaveFrom='opacity-100 scale-100'
leaveTo='opacity-0 scale-95'
>
<Dialog.Panel className='w-full max-w-md overflow-hidden rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all'>
<Dialog.Title
as='h3'
className='text-lg font-medium leading-6 text-gray-900'
>
{i18n.t('common.sessionToFinish')}
</Dialog.Title>
<div className='mt-2'>
<p className='text-lg font-extrabold text-gray-500'>
{countTimer}
</p>
</div>
<div className='mt-4 flex w-full flex-row-reverse'>
<button
type='button'
className='mr-2 inline-flex justify-center rounded-md border border-transparent bg-red-100 px-4 py-2 text-sm font-medium text-red-900 hover:bg-red-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-red-500 focus-visible:ring-offset-2'
onClick={() => { stopIdleMonitor() }}
>
{i18n.t('common.sessionContinue')}
</button>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition>
}
</>
)
}
SessionTimeout.propTypes = {
i18n: PropTypes.object,
user: PropTypes.object,
onTimeout: PropTypes.func,
onCancelTimeout: PropTypes.func
}
SessionTimeout.defaultProps = {
i18n: { t: () => { return 'txtNone' } },
user: {},
onTimeout: () => {},
onCancelTimeout: () => {}
}