import { getActionFromState as getActionFromStateDefault , getStateFromPath as getStateFromPathDefault , NavigationContainerRef , ParamListBase , } from '@react-navigation/core' ; import * as React from 'react' ; import { Linking , Platform } from 'react-native' ; import extractPathFromURL from './extractPathFromURL' ; import type { LinkingOptions } from './types' ; type ResultState = ReturnType < typeof getStateFromPathDefault > ; type Options = LinkingOptions < ParamListBase > & { independent ? : boolean ; } ; let linkingHandlers = [ ] ; export default function useLinking ( ref , { independent , enabled = true , prefixes , filter , config , getInitialURL = ( ) => Promise . race ( [ Linking . getInitialURL ( ) , new Promise < undefined > ( ( resolve ) => setTimeout ( resolve , 150 ) ) , ] ) , subscribe = ( listener ) => { const callback = ( { url } ) => listener ( url ) ; const subscription = Linking . addEventListener ( 'url' , callback ) as { remove ( ) : void ; } | undefined ; return ( ) => { if ( subscription ?. remove ) { subscription . remove ( ) ; } else { Linking . removeEventListener ( 'url' , callback ) ; } } ; } , getStateFromPath = getStateFromPathDefault , getActionFromState = getActionFromStateDefault , } ) { React . useEffect ( ( ) => { if ( process . env . NODE_ENV === 'production' ) { return undefined ; } if ( independent ) { return undefined ; } if ( enabled !== false && linkingHandlers . length ) { console . error ( [ 'Looks like you have configured linking in multiple places. This is likely an error since deep links should only be handled in one place to avoid conflicts. Make sure that:' , "- You don't have multiple NavigationContainers in the app each with 'linking' enabled" , '- Only a single instance of the root component is rendered' , Platform . OS === 'android' ? "- You have set 'android:launchMode=singleTask' in the '<activity />' section of the 'AndroidManifest.xml' file to avoid launching multiple instances" : '' , ] . join ( '\n' ) . trim ( ) ) ; } const handler = Symbol ( ) ; if ( enabled !== false ) { linkingHandlers . push ( handler ) ; } return ( ) => { const index = linkingHandlers . indexOf ( handler ) ; if ( index > - 1 ) { linkingHandlers . splice ( index , 1 ) ; } } ; } , [ enabled , independent ] ) ; const enabledRef = React . useRef ( enabled ) ; const prefixesRef = React . useRef ( prefixes ) ; const filterRef = React . useRef ( filter ) ; const configRef = React . useRef ( config ) ; const getInitialURLRef = React . useRef ( getInitialURL ) ; const getStateFromPathRef = React . useRef ( getStateFromPath ) ; const getActionFromStateRef = React . useRef ( getActionFromState ) ; React . useEffect ( ( ) => { enabledRef . current = enabled ; prefixesRef . current = prefixes ; filterRef . current = filter ; configRef . current = config ; getInitialURLRef . current = getInitialURL ; getStateFromPathRef . current = getStateFromPath ; getActionFromStateRef . current = getActionFromState ; } ) ; const getStateFromURL = React . useCallback ( ( url ) => { if ( ! url || ( filterRef . current && ! filterRef . current ( url ) ) ) { return undefined ; } const path = extractPathFromURL ( prefixesRef . current , url ) ; return path ? getStateFromPathRef . current ( path , configRef . current ) : undefined ; } , [ ] ) ; const getInitialState = React . useCallback ( ( ) => { let state ; if ( enabledRef . current ) { const url = getInitialURLRef . current ( ) ; if ( url != null && typeof url !== 'string' ) { return url . then ( ( url ) => { const state = getStateFromURL ( url ) ; return state ; } ) ; } state = getStateFromURL ( url ) ; } const thenable = { then ( onfulfilled ? ) { return Promise . resolve ( onfulfilled ? onfulfilled ( state ) : state ) ; } , catch ( ) { return thenable ; } , } ; return thenable as PromiseLike < ResultState | undefined > ; } , [ getStateFromURL ] ) ; React . useEffect ( ( ) => { const listener = ( url ) => { if ( ! enabled ) { return ; } const navigation = ref . current ; const state = navigation ? getStateFromURL ( url ) : undefined ; if ( navigation && state ) { const rootState = navigation . getRootState ( ) ; if ( state . routes . some ( ( r ) => ! rootState ?. routeNames . includes ( r . name ) ) ) { console . warn ( "The navigation state parsed from the URL contains routes not present in the root navigator. This usually means that the linking configuration doesn't match the navigation structure. See https://reactnavigation.org/docs/configuring-links for more details on how to specify a linking configuration." ) ; return ; } const action = getActionFromStateRef . current ( state , configRef . current ) ; if ( action !== undefined ) { try { navigation . dispatch ( action ) ; } catch ( e ) { console . warn ( `An error occurred when trying to handle the link '${ url }': ${ e . message }` ) ; } } else { navigation . resetRoot ( state ) ; } } } ; return subscribe ( listener ) ; } , [ enabled , getStateFromURL , ref , subscribe ] ) ; return { getInitialState , } ; }