1import { Component } from "react";
2import { withRouter } from "next/router";
3import debounce from "lodash.debounce";
4import Downshift from "downshift";
5import ob from "urbit-ob";
6import Sigil from "../components/Sigil";
8class Search extends Component {
14 this.searchEndpoint = this.searchEndpoint.bind(this);
15 this.onInputValueChange = this.onInputValueChange.bind(this);
16 this.onSelect = this.onSelect.bind(this);
17 this.patpSearch = this.patpSearch.bind(this);
18 this.devSearch = this.devSearch.bind(this);
19 this.opsSearch = this.opsSearch.bind(this);
22 searchEndpoint(query) {
23 return `/api/search?q=${encodeURIComponent(query)}`;
27 return `/api/dev-search?q=${encodeURIComponent(query)}`;
31 return `/api/ops-search?q=${encodeURIComponent(query)}`;
36 (ob.isValidPatp(`~${deSig(query.toLowerCase())}`) &&
37 `~${deSig(query.toLowerCase())}`.length < 15) ||
38 (!isNaN(query) && query <= 4294967295)
49 this.props.router.push(`${item.url}${item?.slug ? item.slug : ""}`);
50 return this.props.closeSearch();
53 this.props.router.push(item.slug);
54 return this.props.closeSearch();
58 onInputValueChange = debounce(async (query) => {
60 const search = fetch(this.searchEndpoint(query))
61 .then((res) => res.json())
62 .then(async (res) => {
63 return [...res.glossary.map((item) => ({
64 type: "GLOSSARY_RESULT",
66 })), ...res.results.map((item) => ({
72 const patp = this.patpSearch(query)
75 : ob.patp(ob.patp2dec(`~${deSig(query)}`))
78 const patpResult = this.patpSearch(query)
90 const devSearch = fetch(this.devSearch(query))
91 .then((res) => res.json())
93 return [...res.glossary.map((item) => ({
94 type: "GLOSSARY_RESULT",
96 })), ...res.results.map((item) => ({
102 const opsSearch = fetch(this.opsSearch(query))
103 .then((res) => res.json())
105 return [...res.glossary.map((item) => ({
106 type: "GLOSSARY_RESULT",
108 })), ...res.results.map((item) => ({
114 const [results, devResults, opsResults] =
115 await Promise.all([search, devSearch, opsSearch]);
124 const aNum = a?.type === "GLOSSARY_RESULT" ? 1 : -1;
125 const bNum = b?.type === "GLOSSARY_RESULT" ? 1 : -1;
129 this.setState({ results: list });
131 this.setState({ results: [] });
136 const { state, props } = this;
138 if (props.showSearch) {
141 onSelect={(selection) => this.onSelect(selection)}
142 onInputValueChange={(event) => this.onInputValueChange(event)}
143 itemToString={(item) => (item ? item.slug : "")}
144 defaultHighlightedIndex={0}
157 <div className="fixed w-screen h-screen z-50 flex flex-col items-center p-4">
159 onClick={(event) => props.closeSearch(event)}
160 className="top-0 left-0 fixed w-screen h-screen bg-washedWall"
162 <div className="relative flex flex-col max-w-screen-lg md:my-32 w-full md:w-10/12 lg:w-8/12 xl:w-6/12 rounded-xl bg-white min-h-0 overflow-hidden">
164 style={{ display: "inline-block" }}
165 {...getRootProps({}, { suppressRefError: true })}
169 className="text-lg md:text-xl lg:text-2xl font-medium text-green-400 bg-transparent py-2 px-4 outline-none relative w-full"
170 placeholder="Search..."
172 onClick={(e) => e.stopPropagation()}
174 onKeyDown: (event) => {
175 if (event.key === "Escape") {
176 // Prevent Downshift's default 'Escape' behavior.
177 event.nativeEvent.preventDownshiftDefault = true;
178 this.props.closeSearch(event);
184 <ul {...getMenuProps()} className="overflow-y-scroll">
186 ? state.results.map((item, index) => {
187 const selected = highlightedIndex === index;
188 if (item.type === "PATP") {
191 className={`cursor-pointer p-2 flex space-x-2 items-center text-left w-full ${selected ? "bg-green-400" : ""
194 key: item.content.slug + "-" + index,
197 selected: highlightedIndex === index,
200 <div className="rounded-md overflow-hidden">
202 patp={item.content.patp}
207 <p className="font-mono">{item.content.patp}</p>
211 if (item.type === "GLOSSARY_RESULT") {
214 className={`cursor-pointer flex text-left w-full ${selected ? "bg-green-400" : ""
217 key: item.content.slug + "-" + index,
220 selected: highlightedIndex === index,
223 <div className="font-semibold p-3">
225 className={`text-base ${selected ? "text-white" : "text-wall-600"
228 {item.content.symbol.length > 0 && (
230 className={`mr-1 rounded px-1 py-0.5 ${selected
235 {item.content.symbol}
241 className={`font-normal text-base mt-1 ${selected ? "text-white" : "text-wall-600"
243 dangerouslySetInnerHTML={{
244 __html: item.content.desc,
251 if (item.type === "RESULT") {
254 className={`cursor-pointer flex text-left w-full ${selected ? "bg-green-400" : ""
257 key: item.content.link + "-" + index,
263 <div className="p-3">
265 className={`font-medium text-base ${selected ? "text-white" : "text-wall-600"
268 {item.content.parent !== "Content"
269 ? `${item.content.parent} /`
274 className={`text-base font-regular text-small ${selected ? "text-midWhite" : "text-wall-500"
277 {item.content.content}
278 {item?.content?.foundOnPage && (
279 <span className="italic block">
280 Found in page content
288 if (item.type === "DEV_RESULT") {
289 const devItem = Object.assign({}, item.content);
292 ] = `https://developers.urbit.org${item.content.slug}`;
295 className={`cursor-pointer flex text-left w-full ${selected ? "bg-green-400" : ""
298 key: item.content.link + "-" + index,
304 <div className="p-3">
306 className={`font-medium text-base ${selected ? "text-white" : "text-wall-600"
309 <span className="text-wall-400">
310 {item.content.parent !== "Content"
311 ? `developers.urbit.org / ${item.content.parent} /`
312 : "developers.urbit.org /"}{" "}
317 className={`text-base font-regular text-small ${selected ? "text-midWhite" : "text-wall-500"
320 {item.content.content}
321 {item?.content?.foundOnPage && (
322 <span className="italic block">
323 Found in page content
331 if (item.type === "OPS_RESULT") {
332 const opsItem = Object.assign({}, item.content);
335 ] = `https://operators.urbit.org${item.content.slug}`;
338 className={`cursor-pointer flex text-left w-full ${selected ? "bg-green-400" : ""
341 key: item.content.link + "-" + index,
347 <div className="p-3">
349 className={`font-medium text-base ${selected ? "text-white" : "text-wall-600"
352 <span className="text-wall-400">
353 {item.content.parent !== "Content"
354 ? `operators.urbit.org / ${item.content.parent} /`
355 : "operators.urbit.org /"}{" "}
360 className={`text-base font-regular text-small ${selected ? "text-midWhite" : "text-wall-500"
363 {item.content.content}
364 {item?.content?.foundOnPage && (
365 <span className="italic block">
366 Found in page content
389function deSig(string) {
390 return string.startsWith("~") ? string.substring(1) : string;
393export default withRouter(Search);
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list