import { Component, createRef } from "react"; import { withRouter } from "next/router"; import debounce from "lodash.debounce"; import Downshift from "downshift"; import ob from "urbit-ob"; import Sigil from "../components/Sigil"; import levenSort from "leven-sort"; 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); this.devSearch = this.devSearch.bind(this); this.opsSearch = this.opsSearch.bind(this); } searchEndpoint(query) { return `/api/search?q=${query}`; } devSearch(query) { return `/api/dev-search?q=${query}`; } glossarySearch(query) { return `/api/glossary?q=${encodeURIComponent(query)}`; } opsSearch(query) { return `/api/ops-search?q=${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(async (query) => { if (query.length) { const search = fetch(this.searchEndpoint(query)) .then((res) => res.json()) .then(async (res) => { return 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 glossarySearch = fetch(this.glossarySearch(query)) .then((res) => res.json()) .then((res) => { return res.results.map((item) => ({ type: "GLOSSARY_RESULT", content: item, })); }); const devSearch = fetch(this.devSearch(query)) .then((res) => res.json()) .then((res) => { return res.results.map((item) => ({ type: "DEV_RESULT", content: item, })); }); const opsSearch = fetch(this.opsSearch(query)) .then((res) => res.json()) .then((res) => { return res.results.map((item) => ({ type: "OPS_RESULT", content: item, })); }); const [results, glossaryResults, devResults, opsResults] = await Promise.all([search, glossarySearch, devSearch, opsSearch]); const list = [ ...glossaryResults, ...patpResult, ...results, ...devResults, ...opsResults, ]; 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}

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

    {item.content.content}

  • ); } if (item.type === "DEV_RESULT") { const devItem = Object.assign({}, item.content); devItem[ "slug" ] = `https://developers.urbit.org${item.content.slug}`; return (
  • {item.content.parent !== "Content" ? `developers.urbit.org / ${item.content.parent} /` : "developers.urbit.org /"}{" "} {item.content.title}

    {item.content.content}

  • ); } if (item.type === "OPS_RESULT") { const opsItem = Object.assign({}, item.content); opsItem[ "slug" ] = `https://operators.urbit.org${item.content.slug}`; return (
  • {item.content.parent !== "Content" ? `operators.urbit.org / ${item.content.parent} /` : "operators.urbit.org /"}{" "} {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);