import { FocusEventHandler , KeyboardEvent , useCallback , useEffect , useRef , useState } from 'react' ; import { ResolutionError , ResolutionErrorCode } from '@unstoppabledomains/resolution' ; import { DomainStatus , InlineMessage } from '@components' ; import { isValidENSName , isValidETHAddress , isValidETHRecipientAddress , ProviderHandler } from '@services/EthService' ; import { createENSResolutionError , isResolutionError } from '@services/EthService/ens' ; import { selectDefaultNetwork , useSelector } from '@store' ; import { ErrorObject , IReceiverAddress , Network } from '@types' ; import AddressLookupSelector , { LabeledAddress } from './AddressLookupSelector' ; export interface IGeneralLookupFieldComponentProps { error ? : string | ErrorObject ; network : Network ; isResolvingName : boolean ; name : string ; value : IReceiverAddress ; options : LabeledAddress [ ] ; placeholder ? : string ; onBlur ? : FocusEventHandler ; setIsResolvingDomain ( isResolving ) : void ; handleEthAddress ? ( inputString ) : IReceiverAddress ; handleENSName ? ( resolvedAddress , inputString ) : IReceiverAddress ; onSelect ? ( option ) : void ; onChange ? ( input ) : void ; setFieldValue ? ( field , value , shouldValidate ? ) : void ; setFieldTouched ? ( field , touched ? , shouldValidate ? ) : void ; setFieldError ? ( field , value ) : void ; } const GeneralLookupField = ( { error , network , isResolvingName , onBlur , onSelect , onChange , handleEthAddress , handleENSName , setIsResolvingDomain , name , value , options , placeholder , setFieldValue , setFieldTouched , setFieldError } ) => { const ethNetwork = useSelector ( selectDefaultNetwork ) ; const errorMessage = error && Object . prototype . hasOwnProperty . call ( error , 'message' ) ? ( error as ErrorObject ) . message : error ; const errorType = typeof error === 'object' ? error . type : undefined ; const [ resolutionError , setResolutionError ] = useState < ResolutionError > ( ) ; const handleEthAddressDefault = ( inputString ) : IReceiverAddress => { const found = options . find ( ( o ) => o . address === inputString ) ; if ( found ) { return { display : found . label , value : found . address } ; } if ( handleEthAddress ) return handleEthAddress ( inputString ) ; return { display : inputString , value : inputString } ; } ; useEffect ( ( ) => { if ( setFieldError ) { const validationResult = isValidETHRecipientAddress ( value . value , resolutionError ) ; setFieldError ( name , validationResult . success ? undefined : validationResult . message ) ; } } , [ value , resolutionError ] ) ; const handleENSnameDefault = async ( inputString , defaultInput ) : Promise < IReceiverAddress > => { try { const resolvedAddress = await handleDomainResolve ( inputString ) ; if ( ! resolvedAddress ) return defaultInput ; const [ label ] = inputString . split ( '.' ) ; if ( handleENSName ) return handleENSName ( resolvedAddress , inputString ) ; return { display : label , value : resolvedAddress } ; } catch ( err ) { console . debug ( `[GenericLookupField] ${ err }` ) ; return { display : '' , value : '' } ; } } ; const handleNewInput = async ( inputString ) => { setResolutionError ( undefined ) ; if ( ! inputString || ! network || ! network . id || ! options ) { return ; } let contact = { display : inputString , value : inputString } ; if ( isValidETHAddress ( inputString ) ) { contact = handleEthAddressDefault ( inputString ) ; } else if ( isValidENSName ( inputString ) ) { contact = await handleENSnameDefault ( inputString , contact ) ; } if ( setFieldValue ) { setFieldValue ( name , contact , false ) ; } if ( setFieldTouched ) { setFieldTouched ( name , true , false ) ; } } ; const handleDomainResolve = async ( domain ) : Promise < string | undefined | null > => { if ( ! domain || ! network ) { setIsResolvingDomain ( false ) ; setResolutionError ( undefined ) ; return ; } setIsResolvingDomain ( true ) ; setResolutionError ( undefined ) ; try { const provider = new ProviderHandler ( ethNetwork ) ; const resolvedAddress = await provider . resolveName ( domain , network ) ; if ( ! resolvedAddress ) { throw new ResolutionError ( ResolutionErrorCode . UnregisteredDomain , { domain } ) ; } return resolvedAddress ; } catch ( err ) { if ( setFieldValue ) { setFieldValue ( name , { ... value , value : '' } , true ) ; } if ( isResolutionError ( err ) ) { setResolutionError ( err ) ; return ; } const resErr = createENSResolutionError ( err ) ; if ( resErr ) { setResolutionError ( resErr ) ; return ; } throw err ; } finally { setIsResolvingDomain ( false ) ; } } ; const inputValue = useRef < string > ( '' ) ; const GeneralDropdownFieldCallback = useCallback ( ( ) => { const handleInputChange = ( input ) => { inputValue . current = input ; return input ; } ; const handleEnterKeyDown = ( e ) => { if ( e . keyCode === 13 ) { handleNewInput ( inputValue . current ) ; if ( onChange ) onChange ( inputValue . current ) ; } } ; return ( < > < AddressLookupSelector name = { name } value = { value } options = { options } inputValue = { inputValue . current } onInputChange = { handleInputChange } onSelect = { ( option ) => { if ( setFieldValue ) { setFieldValue ( name , option , true ) ; } if ( setFieldTouched ) { setFieldTouched ( name , true , false ) ; } if ( onSelect ) { onSelect ( option ) ; } } } onBlur = { ( e ) => { handleNewInput ( inputValue . current ) ; if ( setFieldTouched ) { setFieldTouched ( name , true , false ) ; } if ( onBlur ) onBlur ( e ) ; if ( onChange ) onChange ( inputValue . current ) ; } } onEnterKeyDown = { handleEnterKeyDown } placeholder = { placeholder } / > { ( value && isValidENSName ( value . value ) ) || isResolvingName ? ( < DomainStatus domain = { value . value } rawAddress = { value . value } isLoading = { isResolvingName } isError = { ! ! errorMessage } resolutionError = { resolutionError } / > ) : ( errorMessage && < InlineMessage type = { errorType } > { errorMessage } < /InlineMessage>)} < />); } , [ value . display , isResolvingName , options , error ] ) ; return < GeneralDropdownFieldCallback />; } ; export default GeneralLookupField ;