import { useEffect , useRef , useState } from 'react' ; import { getFavicon } from '../../helpers/getFavicon' ; import { AnimationType } from '../../enums/AnimationType' ; import { UsePleaseStayOptions } from '../../types/UsePleaseStayOptions' ; import { useInterval } from '../useInterval' ; export const usePleaseStay = ( { titles , animationType = AnimationType . LOOP , interval = 1000 , faviconURIs = [ ] , alwaysRunAnimations = false , } ) : void => { if ( animationType === AnimationType . CASCADE && titles . length > 1 ) { console . warn ( `You are using animation type '${ animationType }' but passed more than one title in the titles array. Only the first title will be used.` ) ; } const [ shouldAnimate , setShouldAnimate ] = useState < boolean > ( false ) ; const [ titleIndex , setTitleIndex ] = useState < number > ( 0 ) ; const [ faviconIndex , setFaviconIndex ] = useState < number > ( 0 ) ; const [ isAppendMode , setIsAppendMode ] = useState < boolean > ( true ) ; const [ faviconURIsState , setFaviconURIsState ] = useState < Array < string > > ( [ ] ) ; const originalDocumentTitle = useRef < string > ( ) ; const originalFaviconHref = useRef < string > ( ) ; const faviconRef = useRef < HTMLLinkElement > ( ) ; const handleVisibilityChange = ( ) => { document . visibilityState === 'visible' ? restoreDefaults ( ) : setShouldAnimate ( true ) ; } ; const runCascadeLogic = ( ) => { document . title = titles [ 0 ] . substring ( 0 , titleIndex ) ; setTitleIndex ( isAppendMode ? titleIndex + 1 : titleIndex - 1 ) ; if ( titleIndex === titles [ 0 ] . length - 1 && isAppendMode ) { setIsAppendMode ( false ) ; } if ( titleIndex - 1 === 0 && ! isAppendMode ) { setIsAppendMode ( true ) ; } } ; const runLoopLogic = ( ) => { document . title = titles [ titleIndex ] ; setTitleIndex ( titleIndex === titles . length - 1 ? 0 : titleIndex + 1 ) ; } ; const modifyDocumentTitle = ( ) => { switch ( animationType ) { case AnimationType . CASCADE : runCascadeLogic ( ) ; return ; case AnimationType . LOOP : default : runLoopLogic ( ) ; return ; } } ; const modifyFavicon = ( ) => { if ( faviconRef && faviconRef . current ) { faviconRef . current . href = faviconURIsState [ faviconIndex ] ; setFaviconIndex ( faviconIndex === faviconURIsState . length - 1 ? 0 : faviconIndex + 1 ) ; } } ; const restoreDefaults = ( ) => { setShouldAnimate ( false ) ; setTimeout ( ( ) => { if ( faviconRef && faviconRef . current && originalDocumentTitle . current && originalFaviconHref . current ) { document . title = originalDocumentTitle . current ; faviconRef . current . href = originalFaviconHref . current ; } } , interval ) ; } ; useEffect ( ( ) => { const favicon = getFavicon ( ) ; if ( favicon === undefined ) { console . warn ( 'We could not find a favicon in your application.' ) ; return ; } originalDocumentTitle . current = document . title ; originalFaviconHref . current = favicon . href ; faviconRef . current = favicon ; setFaviconURIsState ( [ ... faviconURIs , favicon . href ] ) ; document . addEventListener ( 'visibilitychange' , handleVisibilityChange ) ; return ( ) => { document . removeEventListener ( 'visibilitychange' , handleVisibilityChange ) ; } ; } , [ ] ) ; useEffect ( ( ) => { setShouldAnimate ( alwaysRunAnimations ) ; setTitleIndex ( animationType === AnimationType . CASCADE ? 1 : 0 ) ; } , [ animationType , alwaysRunAnimations ] ) ; useInterval ( ( ) => { modifyDocumentTitle ( ) ; faviconURIsState . length > 1 && modifyFavicon ( ) ; } , shouldAnimate ? interval : null ) ; } ;