import { ChangeEvent, KeyboardEvent, useCallback, useEffect, useState, } from 'react' import { getJSON } from '../../../../infrastructure/fetch-json' import useAbortController from '../../../../shared/hooks/use-abort-controller' const LOCAL_AND_DOMAIN_REGEX = /([^@]+)@(.+)/ function matchLocalAndDomain(emailHint: string) { const match = emailHint.match(LOCAL_AND_DOMAIN_REGEX) if (match) { return { local: match[1], domain: match[2] } } else { return { local: null, domain: null } } } type InstitutionInfo = { hostname: string; university: { id: number } } let domainCache = new Map() export function clearDomainCache() { domainCache = new Map() } type AddEmailInputProps = { onChange: (value: string, institution?: InstitutionInfo) => void } export function AddEmailInput({ onChange }: AddEmailInputProps) { const { signal } = useAbortController() const [suggestion, setSuggestion] = useState(null) const [inputValue, setInputValue] = useState(null) const [matchedInstitution, setMatchedInstitution] = useState(null) useEffect(() => { if (inputValue == null) { return } if (matchedInstitution && suggestion === inputValue) { onChange(inputValue, matchedInstitution) } else { onChange(inputValue) } }, [onChange, inputValue, suggestion, matchedInstitution]) const handleEmailChange = useCallback( (event: ChangeEvent) => { const hint = event.target.value setInputValue(hint) const match = matchLocalAndDomain(hint) if (!matchedInstitution?.hostname.startsWith(match.domain)) { setSuggestion(null) } if (!match.domain) { return } if (domainCache.has(match.domain)) { const cachedDomain = domainCache.get(match.domain) setSuggestion(`${match.local}@${cachedDomain.hostname}`) setMatchedInstitution(cachedDomain) return } const query = `?hostname=${match.domain}&limit=1` getJSON(`/institutions/domains${query}`, { signal }) .then(data => { if (!(data && data[0])) { return } const hostname = data[0]?.hostname if (hostname) { domainCache.set(match.domain, data[0]) setSuggestion(`${match.local}@${hostname}`) setMatchedInstitution(data[0]) } else { setSuggestion(null) setMatchedInstitution(null) } }) .catch(error => { setSuggestion(null) setMatchedInstitution(null) console.error(error) }) }, [signal, matchedInstitution] ) const handleKeyDownEvent = useCallback( (event: KeyboardEvent) => { if (event.key === 'Tab' || event.key === 'Enter') { event.preventDefault() if (suggestion) { setInputValue(suggestion) } } }, [suggestion] ) return (
{suggestion || ''}
) }