@@ -2,7 +2,7 @@ import { useEffect, useState } from 'react';
22import { useSelector , useDispatch } from 'react-redux' ;
33import axios from 'axios' ;
44import { ENDPOINTS } from '~/utils/URL' ;
5- import { startForceLogout } from '../../actions/authActions' ;
5+ import { startForceLogout , stopForceLogout } from '../../actions/authActions' ;
66import { useCountdown } from '../../hooks/useCountdown' ;
77import PopUpBar from '../PopUpBar/PopUpBar' ;
88import { getUserProfile } from '../../actions/userProfile' ;
@@ -11,27 +11,130 @@ function PermissionWatcher() {
1111 const dispatch = useDispatch ( ) ;
1212 const { isAuthenticated, forceLogoutAt } = useSelector ( state => state . auth || { } ) ;
1313 const userProfile = useSelector ( state => state . userProfile ) ;
14- const isAcknowledged = userProfile ?. permissions ?. isAcknowledged !== false ;
14+ const isAcknowledged = userProfile ?. permissions ?. isAcknowledged ;
1515 const [ isAckLoading , setIsAckLoading ] = useState ( false ) ;
1616 // Get seconds remaining until force logout
1717 const secondsRemaining = useCountdown ( forceLogoutAt ) ;
18+ const [ wasForceLoggedOut , setWasForceLoggedOut ] = useState ( false ) ;
19+ const [ flagReady , setFlagReady ] = useState ( false ) ;
20+ // Track the initial acknowledged state when user first logs in
21+ const [ initialAcknowledgedState , setInitialAcknowledgedState ] = useState ( null ) ;
22+ // Track if user has just logged in (to distinguish from mid-session changes)
23+ const [ isInitialLogin , setIsInitialLogin ] = useState ( false ) ;
1824
19- // Start the force logout countdown when conditions are met
25+ // On mount or when authentication changes, read flag from sessionStorage
2026 useEffect ( ( ) => {
21- if ( isAuthenticated && ! isAcknowledged && ! forceLogoutAt ) {
22- // eslint-disable-next-line no-console
23- console . log ( 'Starting force logout countdown due to unacknowledged permission changes' ) ;
24- dispatch ( startForceLogout ( 20000 ) ) ; // 20 seconds countdown
27+ if ( isAuthenticated ) {
28+ try {
29+ const flag = sessionStorage . getItem ( 'wasForceLoggedOut' ) ;
30+ setWasForceLoggedOut ( flag === 'true' ) ;
31+ sessionStorage . removeItem ( 'wasForceLoggedOut' ) ;
32+ } catch {
33+ // sessionStorage might not be available (private browsing, etc.)
34+ // Silently fail - component will work without the flag
35+ }
36+
37+ // Mark as initial login (initial state will be captured when profile loads)
38+ setIsInitialLogin ( true ) ;
39+ setInitialAcknowledgedState ( null ) ; // Reset to wait for profile load
40+ } else {
41+ // User logged out, reset state
42+ setIsInitialLogin ( false ) ;
43+ setInitialAcknowledgedState ( null ) ;
44+ setWasForceLoggedOut ( false ) ;
45+ }
46+
47+ setFlagReady ( true ) ;
48+ } , [ isAuthenticated ] ) ;
49+
50+ // Track when user profile is first loaded after login and handle initial login cases
51+ useEffect ( ( ) => {
52+ if ( ! isAuthenticated || ! flagReady ) return ;
53+ if ( userProfile === null || userProfile === undefined ) return ; // Wait for profile to load
54+ if ( ! isInitialLogin ) return ; // Only handle initial login cases
55+
56+ // Capture the initial acknowledged state when profile is first loaded
57+ if ( initialAcknowledgedState === null ) {
58+ setInitialAcknowledgedState ( isAcknowledged ) ;
59+ return ; // Wait for next render to check conditions
60+ }
61+
62+ // Edge Case 2: User permissions changed when logged out → show banner only on login
63+ // Detected by: user just logged in with unacknowledged permissions
64+ // AND was NOT force logged out (just normal logout with permission changes)
65+ const loggedInWithUnacknowledgedPermissions =
66+ ! isAcknowledged && ! forceLogoutAt && ! wasForceLoggedOut ;
67+
68+ if ( loggedInWithUnacknowledgedPermissions ) {
69+ setIsInitialLogin ( false ) ; // Mark as no longer initial login
70+ return ;
71+ }
72+
73+ // Edge Case 3: User was force logged out → permissions change → user logs back in → show banner only
74+ // Detected by: user just logged in with unacknowledged permissions AND was force logged out
75+ const loggedInAfterForceLogout = ! isAcknowledged && ! forceLogoutAt && wasForceLoggedOut ;
76+
77+ if ( loggedInAfterForceLogout ) {
78+ setIsInitialLogin ( false ) ; // Mark as no longer initial login
79+ return ;
2580 }
26- } , [ isAuthenticated , isAcknowledged , forceLogoutAt , dispatch ] ) ;
81+
82+ // If initial login and permissions are acknowledged, mark as no longer initial
83+ if ( isAcknowledged ) {
84+ setIsInitialLogin ( false ) ;
85+ }
86+ } , [
87+ isAuthenticated ,
88+ flagReady ,
89+ isAcknowledged ,
90+ forceLogoutAt ,
91+ wasForceLoggedOut ,
92+ dispatch ,
93+ isInitialLogin ,
94+ initialAcknowledgedState ,
95+ userProfile ,
96+ ] ) ;
97+
98+ // Handle mid-session permission changes (Edge Case 1)
99+ useEffect ( ( ) => {
100+ if ( ! isAuthenticated || ! flagReady ) return ;
101+ if ( userProfile === null || userProfile === undefined ) return ; // Wait for profile to load
102+ if ( isInitialLogin ) return ; // Skip mid-session checks during initial login
103+
104+ // User permissions changed when logged in → start timer
105+ // Detected by: permissions were acknowledged (or was null/true), then became unacknowledged
106+ // AND user was already logged in (not initial login)
107+ const permissionsChangedMidSession =
108+ ! isAcknowledged && ! forceLogoutAt && initialAcknowledgedState !== false ; // Was acknowledged or null before (not explicitly false)
109+
110+ if ( permissionsChangedMidSession ) {
111+ dispatch ( startForceLogout ( 20000 ) ) ;
112+ return ;
113+ }
114+
115+ // Case: permissions re-acknowledged → cancel timer
116+ if ( isAcknowledged && forceLogoutAt ) {
117+ dispatch ( stopForceLogout ( ) ) ;
118+ // Reset initial state since permissions are now acknowledged
119+ setInitialAcknowledgedState ( true ) ;
120+ }
121+ } , [
122+ isAuthenticated ,
123+ flagReady ,
124+ isAcknowledged ,
125+ forceLogoutAt ,
126+ dispatch ,
127+ isInitialLogin ,
128+ initialAcknowledgedState ,
129+ userProfile ,
130+ ] ) ;
131+
27132 // Handle acknowledgment of permission changes
28133 const handleAcknowledge = async ( ) => {
29134 try {
30135 setIsAckLoading ( true ) ;
31136
32137 if ( ! userProfile || ! userProfile . _id ) {
33- // eslint-disable-next-line no-console
34- //console.error('User profile not available');
35138 setIsAckLoading ( false ) ;
36139 return ;
37140 }
@@ -49,6 +152,9 @@ function PermissionWatcher() {
49152 } )
50153 . then ( ( ) => {
51154 setIsAckLoading ( false ) ;
155+ // Update initial state to reflect acknowledgment
156+ setInitialAcknowledgedState ( true ) ;
157+ setIsInitialLogin ( false ) ;
52158 dispatch ( getUserProfile ( _id ) ) ;
53159 } )
54160 . catch ( error => {
@@ -63,21 +169,19 @@ function PermissionWatcher() {
63169 }
64170 } ;
65171
66- // Only render the popup when a force logout is in progress
67- if ( ! forceLogoutAt ) {
68- return null ;
69- }
70- return (
71- ! isAcknowledged && (
172+ // Force logout timer running (mid-session permission change)
173+ if ( forceLogoutAt && ! isAcknowledged ) {
174+ return (
72175 < PopUpBar
73- message = { `Permissions changed—logging out in ${ secondsRemaining } s. Timer will be stopped; please restart after login .` }
176+ message = { `Permissions changed—logging out in ${ secondsRemaining } s unless acknowledged .` }
74177 onClickClose = { handleAcknowledge }
75- textColor = "red"
76178 isLoading = { isAckLoading }
77- button = { false }
179+ button
78180 />
79- )
80- ) ;
181+ ) ;
182+ }
183+
184+ return null ;
81185}
82186
83187export default PermissionWatcher ;
0 commit comments