164 lines
5.9 KiB
JavaScript
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: () => {}
|
|
}
|