@@ -497,6 +497,32 @@ if (userID) {
497497 } ;
498498
499499 const sessionChannel = "BroadcastChannel" in window ? new BroadcastChannel ( sessionChannelName ) : null ;
500+ const recentMessageIds = new Map ( ) ;
501+ const recentMessageTtlMs = 5000 ;
502+ const maxRecentMessageIds = 100 ;
503+
504+ const shouldSkipMessage = ( message ) => {
505+ if ( ! message ?. id ) {
506+ return false ;
507+ }
508+ const now = Date . now ( ) ;
509+ const lastSeen = recentMessageIds . get ( message . id ) ;
510+ if ( lastSeen && now - lastSeen < recentMessageTtlMs ) {
511+ return true ;
512+ }
513+ recentMessageIds . set ( message . id , now ) ;
514+ if ( recentMessageIds . size > maxRecentMessageIds ) {
515+ for ( const [ id , ts ] of recentMessageIds ) {
516+ if ( now - ts > recentMessageTtlMs ) {
517+ recentMessageIds . delete ( id ) ;
518+ }
519+ if ( recentMessageIds . size <= maxRecentMessageIds ) {
520+ break ;
521+ }
522+ }
523+ }
524+ return false ;
525+ } ;
500526
501527 const broadcastSessionEvent = ( type , data = { } ) => {
502528 const message = {
@@ -595,21 +621,31 @@ if (userID) {
595621 if ( ! message || message . from === sessionTabId ) {
596622 return ;
597623 }
624+ if ( shouldSkipMessage ( message ) ) {
625+ return ;
626+ }
598627
599628 sessionDebugLog ( "receive" , message ) ;
600629 if ( message . type === "warning" ) {
601630 const time = Number ( message . data ?. time ) ;
602631 if ( time ) {
603632 setWarningState ( time ) ;
604- }
605- if ( ! isLeader ( ) && window . ProcessMaker . closeSessionModal ) {
606- window . ProcessMaker . closeSessionModal ( ) ;
633+ if ( document . visibilityState === "visible" ) {
634+ showWarningIfActive ( ) ;
635+ }
607636 }
608637 return ;
609638 }
610639
611640 if ( message . type === "renewing" ) {
612- setRenewingState ( ! ! message . data ?. isRenewing ) ;
641+ const isRenewing = ! ! message . data ?. isRenewing ;
642+ setRenewingState ( isRenewing ) ;
643+ if ( isRenewing ) {
644+ clearWarningState ( ) ;
645+ if ( window . ProcessMaker . closeSessionModal ) {
646+ window . ProcessMaker . closeSessionModal ( ) ;
647+ }
648+ }
613649 return ;
614650 }
615651
@@ -692,10 +728,16 @@ if (userID) {
692728 if ( remainingTime <= 0 ) {
693729 sessionDebugLog ( "warning:skip" , { remainingTime } ) ;
694730 clearWarningState ( ) ;
731+ if ( window . ProcessMaker . closeSessionModal ) {
732+ window . ProcessMaker . closeSessionModal ( ) ;
733+ }
695734 setRenewingState ( false ) ;
696735 return ;
697736 }
698737 refreshRenewingStateFromStorage ( ) ;
738+ if ( renewingState ?. isRenewing ) {
739+ return ;
740+ }
699741 sessionDebugLog ( "warning:show" , { remainingTime } ) ;
700742 // Guard for layouts that don't include the session modal.
701743 if ( typeof window . ProcessMaker . sessionModal === "function" ) {
@@ -720,7 +762,12 @@ if (userID) {
720762 now,
721763 } ) ;
722764 if ( isVisible ) {
723- writeLeader ( ) ;
765+ const leaderExpired = ! leader || ( now - leader . ts >= leaderTtlMs ) ;
766+ if ( leaderExpired || leader ?. tabId === sessionTabId ) {
767+ writeLeader ( ) ;
768+ }
769+ refreshWarningStateFromStorage ( ) ;
770+ showWarningIfActive ( ) ;
724771 }
725772
726773 const leaderNow = isLeader ( ) ;
@@ -747,12 +794,13 @@ if (userID) {
747794 setInterval ( updateLeadership , leaderHeartbeatMs ) ;
748795 window . addEventListener ( "visibilitychange" , ( ) => {
749796 updateLeadership ( ) ;
797+ // Keep warning state in sync when switching tabs.
798+ refreshWarningStateFromStorage ( ) ;
799+ showWarningIfActive ( ) ;
750800 if ( isLeader ( ) ) {
751- // Keep warning/timer state in sync when switching tabs .
801+ // Only the leader drives the worker countdown .
752802 refreshSessionStateFromStorage ( ) ;
753- refreshWarningStateFromStorage ( ) ;
754803 startTimeoutWorker ( sessionState . timeout ) ;
755- showWarningIfActive ( ) ;
756804 }
757805 } ) ;
758806
0 commit comments