import { DependencyList , Dispatch , EffectCallback , SetStateAction , useCallback , useEffect , useMemo , useRef , useState } from 'react' ; import useAsyncEffect from 'use-async-effect' ; import useFetch , { CachePolicies } from 'use-http' ; import { API_BLOCKCHAIN_CONTRACT_STORAGE } from '../services/Blockchain' ; import { ContractCallHookReturn , ContractInteractionMethod , ContractOperationStatus } from '../types/Contracts' ; import { MintError , MintProgressMessage , MintResponse } from '../types/Responses' ; import { processTzProfile } from './user' ; export function useIsMounted ( ) { const isMounted = useRef ( false ) ; useEffect ( ( ) => { isMounted . current = true ; return ( ) => { isMounted . current = false ; } ; } , [ ] ) ; return useCallback ( ( ) => isMounted . current , [ ] ) ; } /**
 * useIsMounted() by triggers a state update when it mounts
 */ export function useIsMountedState ( ) { const [ isMounted , setIsMounted ] = useState < boolean > ( false ) ; useEffect ( ( ) => { setIsMounted ( true ) ; return ( ) => { setIsMounted ( false ) ; } ; } , [ ] ) ; return isMounted ; } export const useClientEffect = ( effect , dependencies ? ) : void => { if ( typeof window !== "undefined" ) { useEffect ( effect , dependencies ) ; } } ; export const useClientAsyncEffect = ( effect , dependencies ? ) : void => { if ( typeof window !== "undefined" ) { useAsyncEffect ( effect , dependencies ) ; } } ; export const useInterval = ( callback , intervalMs , triggerStart = false ) => { const isMounted = useIsMounted ( ) ; useEffect ( ( ) => { if ( triggerStart ) callback ( ) ; const interval = setInterval ( ( ) => { if ( isMounted ( ) ) callback ( ) ; } , intervalMs ) ; return ( ) => { clearInterval ( interval ) ; } ; } , [ ] ) ; } ; /**
 * Designed to interract with generic contract methods residing in the Wallet service.
 */ export function useContractCall < T > ( contractMethod ? ) { const [ state , setState ] = useState < ContractOperationStatus > ( ContractOperationStatus . NONE ) ; const [ loading , setLoading ] = useState < boolean > ( false ) ; const [ success , setSuccess ] = useState < boolean > ( false ) ; const [ error , setError ] = useState < boolean > ( false ) ; const [ transactionHash , setTransactionHash ] = useState < string | null > ( null ) ; const counter = useRef < number > ( 0 ) ; const isMounted = useIsMounted ( ) ; const clear = ( ) => { setLoading ( false ) ; setSuccess ( false ) ; setError ( false ) ; setTransactionHash ( null ) ; setState ( ContractOperationStatus . NONE ) ; } ; const call = ( data ) => { setLoading ( true ) ; setSuccess ( false ) ; setError ( false ) ; setTransactionHash ( null ) ; setState ( ContractOperationStatus . NONE ) ; counter . current ++ ; const id = counter . current ; contractMethod && contractMethod ( data , ( opState , opData ) => { if ( counter . current === id && isMounted ( ) ) { setState ( opState ) ; if ( opState === ContractOperationStatus . INJECTED ) { setSuccess ( true ) ; setLoading ( false ) ; if ( opData ) { setTransactionHash ( opData ) ; } } else if ( opState === ContractOperationStatus . ERROR ) { setLoading ( false ) ; setError ( true ) ; } } } ) ; } ; return { state , transactionHash , loading , success , call , clear , error } ; } interface HookMintReturn { progress : MintProgressMessage | null ; error : MintError | null ; success : boolean ; loading : boolean ; data : MintResponse | null ; start : ( ) => void ; } export function useMint ( id ) { const [ counter , setCounter ] = useState ( - 1 ) ; const [ progress , setProgress ] = useState < MintProgressMessage | null > ( null ) ; const [ error , setError ] = useState < MintError | null > ( null ) ; const [ success , setSuccess ] = useState < boolean > ( false ) ; const [ loading , setLoading ] = useState < boolean > ( false ) ; const [ data , setData ] = useState < MintResponse | null > ( null ) ; const start = ( ) => { setCounter ( counter + 1 ) ; } ; const reset = ( ) => { setProgress ( null ) ; setError ( null ) ; setSuccess ( false ) ; setLoading ( false ) ; setData ( null ) ; } ; useEffect ( ( ) => { if ( counter >= 0 ) { reset ( ) ; setLoading ( true ) ; const source = new EventSource ( `${ process . env . NEXT_PUBLIC_API_FILE_ROOT ! }/mint/${ id }` ) ; const progressHandler = ( message ) => { setProgress ( message . data ) ; } ; const errorHandler = ( message ) => { setError ( message . data ) ; setLoading ( false ) ; source . close ( ) ; } ; const successHandler = ( message ) => { setSuccess ( true ) ; setData ( JSON . parse ( message . data ) ) ; setLoading ( false ) ; source . close ( ) ; } ; source . addEventListener ( "progress" , progressHandler ) ; source . addEventListener ( "mint-success" , successHandler ) ; source . addEventListener ( "mint-error" , errorHandler ) ; return ( ) => { source . removeEventListener ( "progress" , progressHandler ) ; source . removeEventListener ( "mint-success" , successHandler ) ; source . removeEventListener ( "mint-error" , errorHandler ) ; source . close ( ) ; } ; } } , [ counter ] ) ; return { progress , error , success , loading , data , start } ; } type EventSourceState = "progress" | "error" | "success" ; interface HookEventSourceReturn < T , P , E > { progress : P | null ; error : E | null ; success : boolean ; loading : boolean ; data : T | null ; start : ( ) => void ; } param url export function useEventSource < T , P = string , E = string > ( url , dataTransform = ( data ) => data ) { const [ counter , setCounter ] = useState ( - 1 ) ; const [ progress , setProgress ] = useState < P | null > ( null ) ; const [ error , setError ] = useState < E | null > ( null ) ; const [ success , setSuccess ] = useState < boolean > ( false ) ; const [ loading , setLoading ] = useState < boolean > ( false ) ; const [ data , setData ] = useState < T | null > ( null ) ; const start = ( ) => { setCounter ( counter + 1 ) ; } ; const reset = ( ) => { setProgress ( null ) ; setError ( null ) ; setSuccess ( false ) ; setLoading ( false ) ; setData ( null ) ; } ; useEffect ( ( ) => { if ( counter >= 0 ) { reset ( ) ; setLoading ( true ) ; const source = new EventSource ( url ) ; const progressHandler = ( message ) => { setProgress ( dataTransform ( message . data ) ) ; } ; const errorHandler = ( message ) => { setError ( message . data ) ; setLoading ( false ) ; source . close ( ) ; } ; const successHandler = ( message ) => { setSuccess ( true ) ; setData ( JSON . parse ( message . data ) ) ; setLoading ( false ) ; source . close ( ) ; } ; source . addEventListener ( "progress" , progressHandler ) ; source . addEventListener ( "success" , successHandler ) ; source . addEventListener ( "error" , errorHandler ) ; return ( ) => { source . removeEventListener ( "progress" , progressHandler ) ; source . removeEventListener ( "mint-success" , successHandler ) ; source . removeEventListener ( "mint-error" , errorHandler ) ; source . close ( ) ; } ; } } , [ counter , url ] ) ; return { progress , error , success , loading , data , start } ; } /**
 * Lazy loads an image
 */ export function useLazyImage ( url ) { const [ loaded , setLoaded ] = useState < boolean > ( false ) ; useEffect ( ( ) => { if ( loaded ) setLoaded ( false ) ; if ( url ) { const img = new Image ( ) ; img . onload = ( ) => { setLoaded ( true ) ; } ; img . src = url ; } } , [ url ] ) ; return loaded ; } /**
 * Verify a use via tz profile
 */ export function useTzProfileVerification ( address ) { const { data : data , post , loading } = useFetch ( 'https://indexer.tzprofiles.com/v1/graphql' , { cachePolicy : CachePolicies . NO_CACHE } ) ; useEffect ( ( ) => { post ( { query : `query MyQuery { tzprofiles_by_pk(account: \"${ address }\") { valid_claims } }` , variables : null , operationName : 'MyQuery' , } ) ; } , [ address ] ) ; const tzProfileData = useMemo ( ( ) => ( ! ! data ?. data ?. tzprofiles_by_pk ?. valid_claims && processTzProfile ( data ?. data ?. tzprofiles_by_pk ?. valid_claims ) ) || null , [ data ] ) ; return { loading , tzProfileData } ; } /**
 * Get contract storage
 */ export function useContractStorage ( address ) { const { data , loading } = useFetch ( API_BLOCKCHAIN_CONTRACT_STORAGE ( address ) , { cachePolicy : CachePolicies . NO_CACHE } , [ ] ) ; return { data , loading } ; } type TAnimationFrameCallback = ( time : number , delta : number ) => void ; /**
 * Creates a request to use animation frame and calls the callback at each tick
 * Responsible for handling dealloc
 */ export function useAnimationFrame ( callback , dependencies ? ) { const started = useRef < number > ( 0 ) ; const lastFrameTime = useRef < number > ( 0 ) ; const requestRef = useRef < number > ( ) ; const loop = ( ) => { requestRef . current = requestAnimationFrame ( loop ) ; const now = performance . now ( ) ; const time = now - started . current ; const delta = now - lastFrameTime . current ; callback ( time , delta ) ; lastFrameTime . current = now ; } ; useEffect ( ( ) => { started . current = lastFrameTime . current = performance . now ( ) ; requestRef . current = requestAnimationFrame ( loop ) ; return ( ) => cancelAnimationFrame ( requestRef . current || 0 ) ; } , dependencies ) ; } /**
 * Returns [true|false] depending on user interracting with the page
 * Whenever the user doesn't interact with the page for X seconds, return true, otherwise
 * returns false
 */ export function useHasInterractedIn ( milliseconds = 2000 , defaultValue = true ) { const timeoutID = useRef < ReturnType < typeof setTimeout > > ( ) ; const [ inter , setInter ] = useState < boolean > ( defaultValue ) ; useEffect ( ( ) => { const listener = ( ) => { timeoutID . current && clearTimeout ( timeoutID . current ) ; setInter ( true ) ; timeoutID . current = setTimeout ( ( ) => { setInter ( false ) ; } , milliseconds ) ; } ; window . addEventListener ( "mousemove" , listener ) ; return ( ) => window . addEventListener ( "mousemove" , listener ) ; } , [ ] ) ; return [ inter , setInter ] ; }