import { Component, createRef } from "react"; import { glossary } from "../lib/glossary"; import { withRouter } from "next/router"; import debounce from "lodash.debounce"; import Downshift from "downshift"; import ob from "urbit-ob"; import Sigil from "../components/Sigil"; class Search extends Component { constructor(props) { super(props); this.state = { results: [], }; this.searchEndpoint = this.searchEndpoint.bind(this); this.onInputValueChange = this.onInputValueChange.bind(this); this.onSelect = this.onSelect.bind(this); this.glossarySearch = this.glossarySearch.bind(this); this.patpSearch = this.patpSearch.bind(this); } searchEndpoint(query) { return `/api/search?q=${query}`; } glossarySearch(query) { return glossary.filter((entry) => { return query.toLowerCase() === entry.name || entry.symbol === query; }); } patpSearch(query) { return ( (ob.isValidPatp(`~${deSig(query.toLowerCase())}`) && `~${deSig(query.toLowerCase())}`.length < 15) || (!isNaN(query) && query <= 4294967295) ); } onSelect(item) { if (item.slug) { this.props.router.push(item.slug); } this.setState({ query: "", results: [], }); this.props.closeSearch(); } onInputValueChange = debounce((query) => { if (query.length) { fetch(this.searchEndpoint(query)) .then((res) => res.json()) .then((res) => { // Wrap results in an object which will tell React what component to use to render results. const results = res.results.map((item) => ({ type: "RESULT", content: item, })); const patp = this.patpSearch(query) ? !isNaN(query) ? ob.patp(query) : ob.patp(ob.patp2dec(`~${deSig(query)}`)) : null; const patpResult = this.patpSearch(query) ? [ { type: "PATP", content: { patp: patp, slug: `/ids/${patp}`, }, }, ] : []; const glossaryResults = this.glossarySearch(query).map((item) => ({ type: "GLOSSARY_RESULT", content: item, })); const list = [...patpResult, ...glossaryResults, ...results]; this.setState({ results: list }); }); } else { this.setState({ results: [] }); } }, 250); render() { const { state, props } = this; if (props.showSearch) { return ( this.onSelect(selection)} onInputValueChange={(event) => this.onInputValueChange(event)} itemToString={(item) => (item ? item.slug : "")} defaultHighlightedIndex={0} > {({ getInputProps, getItemProps, getLabelProps, getMenuProps, isOpen, inputValue, highlightedIndex, selectedItem, getRootProps, }) => (
props.closeSearch(event)} className="top-0 left-0 fixed w-screen h-screen bg-washedWall" />
e.stopPropagation()} {...getInputProps({ onKeyDown: (event) => { if (event.key === "Escape") { // Prevent Downshift's default 'Escape' behavior. event.nativeEvent.preventDownshiftDefault = true; this.props.closeSearch(event); } }, })} />
    {isOpen ? state.results.map((item, index) => { const selected = highlightedIndex === index; if (item.type === "PATP") { return (
  • {item.content.patp}

  • ); } if (item.type === "GLOSSARY_RESULT") { return (
  • {item.content.symbol.length > 0 && ( {item.content.symbol} )} {item.content.name}

    {item.content.desc}

  • ); } if (item.type === "RESULT") { return (
  • {item.content.parent !== "Content" ? `${item.content.parent} /` : ""}{" "} {item.content.title}

    {item.content.content}

  • ); } return null; }) : null}
)} ); } else { return null; } } } function deSig(string) { return string.startsWith("~") ? string.substring(1) : string; } export default withRouter(Search);