@@ -25,6 +25,7 @@ import snackbar from '@/services/SnackbarUtils';
25
25
import { keycloak , logout } from '@/services/management/auth' ;
26
26
import './index.scss' ;
27
27
import { Box } from '@mui/system' ;
28
+ import { toInteger } from 'lodash' ;
28
29
29
30
const LoginView = lazy ( ( ) => import ( '../LoginView' ) ) ;
30
31
const TermsView = lazy ( ( ) => import ( '../TermsView' ) ) ;
@@ -42,9 +43,40 @@ const LinkOutView = lazy(() => import('../LinkOutView'));
42
43
const TemplateView = lazy ( ( ) => import ( '../TemplateView' ) ) ;
43
44
const ProjectsView = lazy ( ( ) => import ( '../ProjectsView' ) ) ;
44
45
46
+ function formatTime ( seconds ) {
47
+ const hours = Math . floor ( seconds / 3600 ) ;
48
+ const minutes = Math . floor ( ( seconds % 3600 ) / 60 ) ;
49
+ const remainingSeconds = seconds % 60 ;
50
+ return `${ hours . toString ( ) . padStart ( 2 , '0' ) } :${ minutes . toString ( ) . padStart ( 2 , '0' ) } :${ remainingSeconds . toString ( ) . padStart ( 2 , '0' ) } ` ;
51
+ }
52
+
53
+ const CountDown = memo ( ( { startingTime } : {
54
+ startingTime : number ;
55
+ } ) => {
56
+ const [ seconds , setSeconds ] = useState ( 0 ) ;
57
+ useEffect ( ( ) => {
58
+ setSeconds ( startingTime ) ;
59
+ } , [ startingTime ] ) ;
60
+
61
+ useEffect ( ( ) => {
62
+ const intervalId = setInterval ( ( ) => {
63
+ setSeconds ( ( s ) => {
64
+ if ( s > 0 ) {
65
+ return s - 1 ;
66
+ }
67
+ clearInterval ( intervalId ) ;
68
+ return 0 ;
69
+ } ) ;
70
+ } , 1000 ) ; // countdown interval of 1 second
71
+
72
+ return ( ) => clearInterval ( intervalId ) ; // cleanup on unmount
73
+ } , [ startingTime ] ) ;
74
+
75
+ return < span > { formatTime ( seconds ) } </ span > ;
76
+ } ) ;
77
+
45
78
// What fraction of TIME ELAPSED should the user be notified of expiring token
46
79
const TIMEOUT_FRACTION = 0.9 ;
47
- const MIN_TIMEOUT = 60000 ;
48
80
49
81
type TimeoutModalPropTypes = {
50
82
authorizationToken : string ;
@@ -54,6 +86,8 @@ type TimeoutModalPropTypes = {
54
86
const TimeoutModal = memo ( ( { authorizationToken, setAuthorizationToken } : TimeoutModalPropTypes ) => {
55
87
const { location : { key : locationKey } } = useHistory ( ) ;
56
88
const [ open , setIsOpen ] = useState ( false ) ;
89
+ // Seconds in which to show in the countdown component
90
+ const [ countDown , setCountDown ] = useState ( 0 ) ;
57
91
const [ isLoading , setIsLoading ] = useState ( false ) ;
58
92
const timerRef = useRef ( null ) ;
59
93
@@ -71,12 +105,15 @@ const TimeoutModal = memo(({ authorizationToken, setAuthorizationToken }: Timeou
71
105
// First load is untracked, until authorizationToken changes
72
106
useEffect ( ( ) => {
73
107
if ( authorizationToken ) {
74
- // Depending on KC setting, whichever one of these token expire will cause a 400 for refresh, so we take the lower one
75
- const leastTimeToExp = ( Math . min ( keycloak . tokenParsed . exp , keycloak . refreshTokenParsed . exp ) * 1000 - Date . now ( ) ) * TIMEOUT_FRACTION ;
76
- // Minimum 1 min timeout timer
77
- const timeout = Math . max ( leastTimeToExp , MIN_TIMEOUT ) ;
108
+ // Ms in which token will expire
109
+ const minTimeToExpire = Math . min ( keycloak . tokenParsed . exp , keycloak . refreshTokenParsed . exp ) * 1000 ;
110
+
111
+ const timeToShowModal = ( minTimeToExpire - Date . now ( ) ) * TIMEOUT_FRACTION ;
78
112
79
- timerRef . current = setTimeout ( ( ) => { setIsOpen ( true ) ; } , timeout ) ;
113
+ timerRef . current = setTimeout ( ( ) => {
114
+ setIsOpen ( true ) ;
115
+ setCountDown ( toInteger ( ( minTimeToExpire - Date . now ( ) ) / 1000 ) ) ;
116
+ } , timeToShowModal ) ;
80
117
}
81
118
return ( ) => {
82
119
if ( timerRef . current ) { clearTimeout ( timerRef . current ) ; }
@@ -105,7 +142,11 @@ const TimeoutModal = memo(({ authorizationToken, setAuthorizationToken }: Timeou
105
142
< Dialog open = { open } onClose = { handleClose } onBackdropClick = { null } >
106
143
< DialogTitle > Session Timeout Notification</ DialogTitle >
107
144
< DialogContent >
108
- < p > Your session is about to expire, would you like to remain logged in?</ p >
145
+ < p >
146
+ { 'Your session is about to expire in ' }
147
+ < CountDown startingTime = { countDown } />
148
+ , would you like to remain logged in?
149
+ </ p >
109
150
</ DialogContent >
110
151
< DialogActions >
111
152
< Button disabled = { isLoading } onClick = { logout } > Logout</ Button >
0 commit comments