@@ -355,6 +355,8 @@ if (userID) {
355355 const sessionLeaderKey = "pm:session:leader" ;
356356 const sessionStateKey = "pm:session:state" ;
357357 const sessionWarningKey = "pm:session:warning" ;
358+ // Track keep-alive progress across tabs.
359+ const sessionRenewingKey = "pm:session:renewing" ;
358360 const sessionMessageKey = "pm:session:message" ;
359361 const sessionTabId = `${ Date . now ( ) } -${ Math . random ( ) . toString ( 16 ) . slice ( 2 ) } ` ;
360362 const leaderHeartbeatMs = 4000 ;
@@ -438,6 +440,7 @@ if (userID) {
438440 } ;
439441
440442 let warningState = readStorageJson ( sessionWarningKey ) ;
443+ let renewingState = readStorageJson ( sessionRenewingKey ) ;
441444
442445 const refreshWarningStateFromStorage = ( ) => {
443446 const storedWarningState = readStorageJson ( sessionWarningKey ) ;
@@ -465,6 +468,34 @@ if (userID) {
465468 sessionDebugLog ( "warning-state:clear" ) ;
466469 } ;
467470
471+ const syncRenewingUi = ( ) => {
472+ // Only update if the navbar component exists in this layout.
473+ if ( window . ProcessMaker . navbar ) {
474+ window . ProcessMaker . navbar . sessionIsRenewing = ! ! renewingState ;
475+ }
476+ } ;
477+
478+ const refreshRenewingStateFromStorage = ( ) => {
479+ const storedRenewingState = readStorageJson ( sessionRenewingKey ) ;
480+ renewingState = storedRenewingState ?. isRenewing ? storedRenewingState : null ;
481+ syncRenewingUi ( ) ;
482+ return renewingState ;
483+ } ;
484+
485+ const setRenewingState = ( isRenewing ) => {
486+ if ( isRenewing ) {
487+ renewingState = {
488+ isRenewing : true ,
489+ ts : Date . now ( ) ,
490+ } ;
491+ writeStorageJson ( sessionRenewingKey , renewingState ) ;
492+ } else {
493+ renewingState = null ;
494+ removeStorageKey ( sessionRenewingKey ) ;
495+ }
496+ syncRenewingUi ( ) ;
497+ } ;
498+
468499 const sessionChannel = "BroadcastChannel" in window ? new BroadcastChannel ( sessionChannelName ) : null ;
469500
470501 const broadcastSessionEvent = ( type , data = { } ) => {
@@ -577,9 +608,15 @@ if (userID) {
577608 return ;
578609 }
579610
611+ if ( message . type === "renewing" ) {
612+ setRenewingState ( ! ! message . data ?. isRenewing ) ;
613+ return ;
614+ }
615+
580616 if ( message . type === "renewed" || message . type === "started" || message . type === "activity" ) {
581617 const timeout = Number ( message . data ?. timeout ) || window . ProcessMaker . AccountTimeoutLength ;
582618 clearWarningState ( ) ;
619+ setRenewingState ( false ) ;
583620 setSessionState ( timeout ) ;
584621 if ( window . ProcessMaker . closeSessionModal ) {
585622 window . ProcessMaker . closeSessionModal ( ) ;
@@ -592,6 +629,7 @@ if (userID) {
592629
593630 if ( message . type === "expired" ) {
594631 clearWarningState ( ) ;
632+ setRenewingState ( false ) ;
595633 window . location = "/logout?timeout=true" ;
596634 }
597635 } ;
@@ -654,8 +692,10 @@ if (userID) {
654692 if ( remainingTime <= 0 ) {
655693 sessionDebugLog ( "warning:skip" , { remainingTime } ) ;
656694 clearWarningState ( ) ;
695+ setRenewingState ( false ) ;
657696 return ;
658697 }
698+ refreshRenewingStateFromStorage ( ) ;
659699 sessionDebugLog ( "warning:show" , { remainingTime } ) ;
660700 // Guard for layouts that don't include the session modal.
661701 if ( typeof window . ProcessMaker . sessionModal === "function" ) {
0 commit comments