Skip to content

Commit da93db6

Browse files
authored
Merge pull request #557 from bcgsc/feat/DEVSU-2370-add-countdown-to-timeout-modal
[DEVSU-2370] add countdown to timeout modal
2 parents 8513d4a + adc7633 commit da93db6

File tree

1 file changed

+48
-7
lines changed

1 file changed

+48
-7
lines changed

app/views/MainView/index.tsx

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import snackbar from '@/services/SnackbarUtils';
2525
import { keycloak, logout } from '@/services/management/auth';
2626
import './index.scss';
2727
import { Box } from '@mui/system';
28+
import { toInteger } from 'lodash';
2829

2930
const LoginView = lazy(() => import('../LoginView'));
3031
const TermsView = lazy(() => import('../TermsView'));
@@ -42,9 +43,40 @@ const LinkOutView = lazy(() => import('../LinkOutView'));
4243
const TemplateView = lazy(() => import('../TemplateView'));
4344
const ProjectsView = lazy(() => import('../ProjectsView'));
4445

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+
4578
// What fraction of TIME ELAPSED should the user be notified of expiring token
4679
const TIMEOUT_FRACTION = 0.9;
47-
const MIN_TIMEOUT = 60000;
4880

4981
type TimeoutModalPropTypes = {
5082
authorizationToken: string;
@@ -54,6 +86,8 @@ type TimeoutModalPropTypes = {
5486
const TimeoutModal = memo(({ authorizationToken, setAuthorizationToken }: TimeoutModalPropTypes) => {
5587
const { location: { key: locationKey } } = useHistory();
5688
const [open, setIsOpen] = useState(false);
89+
// Seconds in which to show in the countdown component
90+
const [countDown, setCountDown] = useState(0);
5791
const [isLoading, setIsLoading] = useState(false);
5892
const timerRef = useRef(null);
5993

@@ -71,12 +105,15 @@ const TimeoutModal = memo(({ authorizationToken, setAuthorizationToken }: Timeou
71105
// First load is untracked, until authorizationToken changes
72106
useEffect(() => {
73107
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;
78112

79-
timerRef.current = setTimeout(() => { setIsOpen(true); }, timeout);
113+
timerRef.current = setTimeout(() => {
114+
setIsOpen(true);
115+
setCountDown(toInteger((minTimeToExpire - Date.now()) / 1000));
116+
}, timeToShowModal);
80117
}
81118
return () => {
82119
if (timerRef.current) { clearTimeout(timerRef.current); }
@@ -105,7 +142,11 @@ const TimeoutModal = memo(({ authorizationToken, setAuthorizationToken }: Timeou
105142
<Dialog open={open} onClose={handleClose} onBackdropClick={null}>
106143
<DialogTitle>Session Timeout Notification</DialogTitle>
107144
<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>
109150
</DialogContent>
110151
<DialogActions>
111152
<Button disabled={isLoading} onClick={logout}>Logout</Button>

0 commit comments

Comments
 (0)