import { useContext, useEffect, useRef, useState } from "react"; import styled from "styled-components"; import { config } from "../../config"; import { SertoUiContext, SertoUiContextInterface } from "../../context/SertoUiContext"; import { Identifier, DidSearchResult, SelectedDid } from "../../types"; import { Launch } from "@rimble/icons"; import { Box, Flex, Input } from "rimble-ui"; import { baseColors, colors, fonts } from "../../themes"; import { DidSearchOption, DidSearchOptionDid } from "./DidSearchOption"; import { SearchIcon } from "./Icons/SearchIcon"; const StyledLink = styled.a` color: ${colors.primary.base}; font-family: ${fonts.sansSerif}; font-size: 14px; font-weight: 600; text-decoration: none; &:hover { text-decoration: underline; } `; export interface DidSearchProps { defaultSelectedDid?: string; identifiers?: Identifier[]; required?: boolean; onChange(value: SelectedDid): void; onBlur?(): void; } export const DidSearch: React.FunctionComponent = (props) => { const { defaultSelectedDid, identifiers, onChange, required, onBlur } = props; const node = useRef() as React.MutableRefObject; const context = useContext(SertoUiContext); const searchService = context.searchService; const [value, setValue] = useState(defaultSelectedDid || ""); const [isOpen, setIsOpen] = useState(false); const [internalResults, setInternalResults] = useState(); const [externalResults, setExternalResults] = useState(); async function searchExternalIdentifiers(search: string) { try { const results = await searchService.getEntries(search); setExternalResults(results); } catch (err) { console.error("failed to get identifiers:", err); return; } } const searchInternalIdentifiers = (search: string) => { const res = (identifiers || context.userDids)?.filter((obj) => Object.values(obj).some((val) => val.includes(search)), ); setInternalResults(res); }; const onClickOutside = (e: any) => { if (node.current.contains(e.target)) { return; } setIsOpen(false); }; useEffect(() => { document.addEventListener("mousedown", onClickOutside); return () => { document.removeEventListener("mousedown", onClickOutside); }; }); return ( ) => { onChange({ did: event.target.value }); setValue(event.target.value); searchInternalIdentifiers(event.target.value); searchExternalIdentifiers(event.target.value); !isOpen && setIsOpen(true); }} borderColor={isOpen && colors.primary.base} borderBottomColor={isOpen && colors.midGray} p="16px 16px 16px 40px" placeholder="Search by domain or DID" required={required} type="url" value={value} width="100%" onBlur={ onBlur && (() => { // Dumb, but since blur happens on mousedown, and onclick selection of DID from dropdown happens on mouseup, blur can get called before the DID is selected. In IssueVcForm input, blur may cause "messaging unsupported" warning to show which changes position of DidSelect and so the selection from DID dropdown doesn't line up with mouse cursor any more. And anyway, conceptually, DidSearch shouldn't fire onBlur right before selection - should fire after. setTimeout(onBlur, 500); }) } /> {((externalResults && externalResults.length > 0) || (internalResults && internalResults.length > 0)) && isOpen && ( {internalResults && internalResults.length > 0 && ( {internalResults.map((did, i: number) => { return ( { onChange(selectedDid); setValue(selectedDid.did); setIsOpen(false); }} did={{ did: did.did, // @TODO/tobek Is this how we'll detect messaging support for now? messagingSupported: !!did.services?.length, }} alias={did.alias} /> ); })} )} {externalResults && externalResults.length > 0 && ( {externalResults.map((result: DidSearchResult, i: number) => { return ( { onChange(selectedDid); setValue(selectedDid.did); setIsOpen(false); }} didDocs={result.didDocs} domain={result.domain} /> ); })} )} Go to Serto Search )} ); };