@@ -275,6 +275,8 @@ export const StickyBottomBanner = ({
275275 ) ;
276276
277277 const [ SelectedBanner , setSelectedBanner ] = useState < MaybeFC | null > ( null ) ;
278+ const [ hasPickMessageCompleted , setHasPickMessageCompleted ] =
279+ useState < boolean > ( false ) ;
278280 const [ asyncArticleCounts , setAsyncArticleCounts ] =
279281 useState < Promise < ArticleCounts | undefined > > ( ) ;
280282
@@ -365,9 +367,10 @@ export const StickyBottomBanner = ({
365367 } ;
366368
367369 pickMessage ( bannerConfig , renderingTarget )
368- . then ( ( PickedBanner : ( ) => MaybeFC ) =>
369- setSelectedBanner ( PickedBanner ) ,
370- )
370+ . then ( ( PickedBanner : ( ) => MaybeFC ) => {
371+ setSelectedBanner ( PickedBanner ) ;
372+ setHasPickMessageCompleted ( true ) ;
373+ } )
371374 . catch ( ( e ) => {
372375 // Report error to Sentry
373376 const msg = `StickyBottomBanner pickMessage - error: ${ String (
@@ -377,6 +380,7 @@ export const StickyBottomBanner = ({
377380 new Error ( msg ) ,
378381 'sticky-bottom-banner' ,
379382 ) ;
383+ setHasPickMessageCompleted ( true ) ;
380384 } ) ;
381385 } , [
382386 isSignedIn ,
@@ -402,6 +406,14 @@ export const StickyBottomBanner = ({
402406 isInAuxiaControlGroup ,
403407 ] ) ;
404408
409+ // Dispatches 'banner:none' event for mobile sticky ad integration (see @guardian/commercial-dev).
410+ // Ensures ads only insert when no banner will be shown.
411+ // hasPickMessageCompleted distinguishes between initial state (not picked yet) and final state (picked nothing).
412+ useEffect ( ( ) => {
413+ if ( hasPickMessageCompleted && SelectedBanner == null ) {
414+ document . dispatchEvent ( new CustomEvent ( 'banner:none' ) ) ;
415+ }
416+ } , [ SelectedBanner , hasPickMessageCompleted ] ) ;
405417 if ( SelectedBanner ) {
406418 return < SelectedBanner /> ;
407419 }
0 commit comments