@@ -565,39 +565,7 @@ export const PiholeWidget = (props: { config?: PiholeWidgetConfig }) => {
565565 setDisableMenuAnchor ( null ) ;
566566
567567 try {
568- // Immediately update UI state for better responsiveness
569- setIsBlocking ( false ) ;
570-
571- // For timed disables, immediately set up the timer view
572- if ( seconds !== null ) {
573- const endTime = new Date ( ) ;
574- endTime . setSeconds ( endTime . getSeconds ( ) + seconds ) ;
575- setDisableEndTime ( endTime ) ;
576-
577- // Calculate and set remaining time immediately
578- const diffSec = seconds ;
579- const minutes = Math . floor ( diffSec / 60 ) ;
580- const secs = diffSec % 60 ;
581- setRemainingTime ( `${ minutes } :${ secs . toString ( ) . padStart ( 2 , '0' ) } ` ) ;
582-
583- // Update stats with timer
584- setStats ( prevStats => ( {
585- ...prevStats ,
586- status : 'disabled' ,
587- timer : seconds
588- } ) ) ;
589- } else {
590- // For indefinite disables, clear timer
591- setDisableEndTime ( null ) ;
592- setRemainingTime ( '' ) ;
593-
594- // Update stats for indefinite disable
595- setStats ( prevStats => ( {
596- ...prevStats ,
597- status : 'disabled' ,
598- timer : null
599- } ) ) ;
600- }
568+ // Don't update UI state here - wait for API call to complete
601569
602570 // Call the backend API to disable blocking
603571 const result = await DashApi . disablePihole (
@@ -612,13 +580,47 @@ export const PiholeWidget = (props: { config?: PiholeWidgetConfig }) => {
612580 ) ;
613581
614582 if ( result ) {
583+ // Only update UI after successful API call
584+ setIsBlocking ( false ) ;
585+
586+ // For timed disables, set up the timer view
587+ if ( seconds !== null ) {
588+ const endTime = new Date ( ) ;
589+ endTime . setSeconds ( endTime . getSeconds ( ) + seconds ) ;
590+ setDisableEndTime ( endTime ) ;
591+
592+ // Calculate and set remaining time immediately
593+ const diffSec = seconds ;
594+ const minutes = Math . floor ( diffSec / 60 ) ;
595+ const secs = diffSec % 60 ;
596+ setRemainingTime ( `${ minutes } :${ secs . toString ( ) . padStart ( 2 , '0' ) } ` ) ;
597+
598+ // Update stats with timer
599+ setStats ( prevStats => ( {
600+ ...prevStats ,
601+ status : 'disabled' ,
602+ timer : seconds
603+ } ) ) ;
604+ } else {
605+ // For indefinite disables, clear timer
606+ setDisableEndTime ( null ) ;
607+ setRemainingTime ( '' ) ;
608+
609+ // Update stats for indefinite disable
610+ setStats ( prevStats => ( {
611+ ...prevStats ,
612+ status : 'disabled' ,
613+ timer : null
614+ } ) ) ;
615+ }
616+
615617 // For Pi-hole v6, trigger an immediate status check
616618 if ( isPiholeV6 ) {
617619 checkPiholeStatus ( ) ;
618620 } else {
619621 // For Pi-hole v5, handle the local timer
620622 if ( seconds !== null ) {
621- // Clear any existing timer
623+ // Clear any existing timer
622624 if ( disableTimer ) {
623625 clearTimeout ( disableTimer ) ;
624626 }
@@ -643,7 +645,7 @@ export const PiholeWidget = (props: { config?: PiholeWidgetConfig }) => {
643645 throw new Error ( 'Failed to disable Pi-hole blocking' ) ;
644646 }
645647 } catch ( err : any ) {
646- // On error, revert UI state
648+ // Error handling remains the same
647649 setIsBlocking ( true ) ;
648650 setDisableEndTime ( null ) ;
649651 setRemainingTime ( '' ) ;
@@ -672,11 +674,14 @@ export const PiholeWidget = (props: { config?: PiholeWidgetConfig }) => {
672674
673675 // Handle enable blocking
674676 const handleEnableBlocking = useCallback ( async ( ) => {
677+ if ( isDisablingBlocking ) return ; // Prevent action if we're already handling a state change
678+
679+ setIsDisablingBlocking ( true ) ; // Use the same lock for enable operations
680+
675681 try {
676682 setIsLoading ( true ) ;
677- // Optimistically update UI
678- setIsBlocking ( true ) ;
679683
684+ // Call API first, don't update UI state optimistically
680685 await DashApi . enablePihole ( {
681686 host : piholeConfig . host ,
682687 port : piholeConfig . port ,
@@ -685,6 +690,17 @@ export const PiholeWidget = (props: { config?: PiholeWidgetConfig }) => {
685690 password : piholeConfig . password
686691 } ) ;
687692
693+ // Only update UI after successful API call
694+ setIsBlocking ( true ) ;
695+ setDisableEndTime ( null ) ;
696+ setRemainingTime ( '' ) ;
697+
698+ // Clear any disable timer
699+ if ( disableTimer ) {
700+ clearTimeout ( disableTimer ) ;
701+ setDisableTimer ( null ) ;
702+ }
703+
688704 setIsLoading ( false ) ;
689705 // Fetch updated stats after enabling
690706 await checkPiholeStatus ( ) ;
@@ -710,8 +726,10 @@ export const PiholeWidget = (props: { config?: PiholeWidgetConfig }) => {
710726 // Generic error
711727 setError ( err . message || 'Failed to enable Pi-hole blocking' ) ;
712728 }
729+ } finally {
730+ setIsDisablingBlocking ( false ) ;
713731 }
714- } , [ piholeConfig , checkPiholeStatus ] ) ;
732+ } , [ piholeConfig , checkPiholeStatus , disableTimer , isDisablingBlocking ] ) ;
715733
716734 // Clean up disable timer on unmount
717735 useEffect ( ( ) => {
@@ -995,6 +1013,7 @@ export const PiholeWidget = (props: { config?: PiholeWidgetConfig }) => {
9951013 height : 25 ,
9961014 fontSize : '0.7rem' ,
9971015 color : 'white' ,
1016+ minWidth : '80px' , // Add fixed minimum width to prevent size changes
9981017 '&:hover' : {
9991018 backgroundColor : 'rgba(255, 255, 255, 0.1)'
10001019 } ,
0 commit comments