1*89d2c67fSBartosz Kaszubowskiimport { SearchIcon, theme, XIcon } from '@expo/styleguide';
2*89d2c67fSBartosz Kaszubowskiimport { Command } from 'cmdk';
3*89d2c67fSBartosz Kaszubowskiimport { useEffect, useState } from 'react';
4*89d2c67fSBartosz Kaszubowskiimport type { Dispatch, SetStateAction } from 'react';
5*89d2c67fSBartosz Kaszubowski
6*89d2c67fSBartosz Kaszubowskiimport { BarLoader } from './BarLoader';
7*89d2c67fSBartosz Kaszubowskiimport { CommandFooter } from './CommandFooter';
8*89d2c67fSBartosz Kaszubowskiimport { RNDirectoryItem, RNDocsItem, ExpoDocsItem, ExpoItem } from './Items';
9*89d2c67fSBartosz Kaszubowskiimport { entries } from './expoEntries';
10*89d2c67fSBartosz Kaszubowskiimport { searchIconStyle, closeIconStyle } from './styles';
11*89d2c67fSBartosz Kaszubowskiimport type { ExpoItemType, RNDirectoryItemType, AlgoliaItemType } from './types';
12*89d2c67fSBartosz Kaszubowskiimport { getExpoDocsResults, getRNDocsResults, getDirectoryResults, getItemsAsync } from './utils';
13*89d2c67fSBartosz Kaszubowski
14*89d2c67fSBartosz Kaszubowskiimport { CALLOUT } from '~/ui/components/Text';
15*89d2c67fSBartosz Kaszubowski
16*89d2c67fSBartosz Kaszubowskitype Props = {
17*89d2c67fSBartosz Kaszubowski  version: string;
18*89d2c67fSBartosz Kaszubowski  open: boolean;
19*89d2c67fSBartosz Kaszubowski  setOpen: Dispatch<SetStateAction<boolean>>;
20*89d2c67fSBartosz Kaszubowski};
21*89d2c67fSBartosz Kaszubowski
22*89d2c67fSBartosz Kaszubowskiexport const CommandMenu = ({ version, open, setOpen }: Props) => {
23*89d2c67fSBartosz Kaszubowski  const [loading, setLoading] = useState(false);
24*89d2c67fSBartosz Kaszubowski  const [query, setQuery] = useState('');
25*89d2c67fSBartosz Kaszubowski  const [expoDocsItems, setExpoDocsItems] = useState<AlgoliaItemType[]>([]);
26*89d2c67fSBartosz Kaszubowski  const [expoItems, setExpoItems] = useState<ExpoItemType[]>([]);
27*89d2c67fSBartosz Kaszubowski  const [rnDocsItems, setRnDocsItems] = useState<AlgoliaItemType[]>([]);
28*89d2c67fSBartosz Kaszubowski  const [directoryItems, setDirectoryItems] = useState<RNDirectoryItemType[]>([]);
29*89d2c67fSBartosz Kaszubowski
30*89d2c67fSBartosz Kaszubowski  const getExpoDocsItems = async () =>
31*89d2c67fSBartosz Kaszubowski    getItemsAsync(query, getExpoDocsResults, setExpoDocsItems, version);
32*89d2c67fSBartosz Kaszubowski  const getRNDocsItems = async () => getItemsAsync(query, getRNDocsResults, setRnDocsItems);
33*89d2c67fSBartosz Kaszubowski  const getDirectoryItems = async () =>
34*89d2c67fSBartosz Kaszubowski    getItemsAsync(query, getDirectoryResults, setDirectoryItems);
35*89d2c67fSBartosz Kaszubowski
36*89d2c67fSBartosz Kaszubowski  const getExpoItems = async () => {
37*89d2c67fSBartosz Kaszubowski    setExpoItems(entries.filter(entry => entry.label.toLowerCase().includes(query.toLowerCase())));
38*89d2c67fSBartosz Kaszubowski  };
39*89d2c67fSBartosz Kaszubowski
40*89d2c67fSBartosz Kaszubowski  const dismiss = () => setOpen(false);
41*89d2c67fSBartosz Kaszubowski
42*89d2c67fSBartosz Kaszubowski  const fetchData = () => {
43*89d2c67fSBartosz Kaszubowski    Promise.all([getExpoDocsItems(), getRNDocsItems(), getDirectoryItems(), getExpoItems()]).then(
44*89d2c67fSBartosz Kaszubowski      () => setLoading(false)
45*89d2c67fSBartosz Kaszubowski    );
46*89d2c67fSBartosz Kaszubowski  };
47*89d2c67fSBartosz Kaszubowski
48*89d2c67fSBartosz Kaszubowski  const onQueryChange = () => {
49*89d2c67fSBartosz Kaszubowski    setLoading(true);
50*89d2c67fSBartosz Kaszubowski    const inputTimeout = setTimeout(fetchData, 150);
51*89d2c67fSBartosz Kaszubowski    return () => clearTimeout(inputTimeout);
52*89d2c67fSBartosz Kaszubowski  };
53*89d2c67fSBartosz Kaszubowski
54*89d2c67fSBartosz Kaszubowski  useEffect(onQueryChange, [query]);
55*89d2c67fSBartosz Kaszubowski
56*89d2c67fSBartosz Kaszubowski  const totalCount =
57*89d2c67fSBartosz Kaszubowski    expoDocsItems.length + rnDocsItems.length + directoryItems.length + expoItems.length;
58*89d2c67fSBartosz Kaszubowski
59*89d2c67fSBartosz Kaszubowski  return (
60*89d2c67fSBartosz Kaszubowski    <Command.Dialog open={open} onOpenChange={setOpen} label="Search Menu" shouldFilter={false}>
61*89d2c67fSBartosz Kaszubowski      <SearchIcon color={theme.icon.secondary} css={searchIconStyle} />
62*89d2c67fSBartosz Kaszubowski      <XIcon color={theme.icon.secondary} css={closeIconStyle} onClick={() => setOpen(false)} />
63*89d2c67fSBartosz Kaszubowski      <Command.Input value={query} onValueChange={setQuery} placeholder="search anything…" />
64*89d2c67fSBartosz Kaszubowski      <BarLoader isLoading={loading} />
65*89d2c67fSBartosz Kaszubowski      <Command.List>
66*89d2c67fSBartosz Kaszubowski        {expoDocsItems.length > 0 && (
67*89d2c67fSBartosz Kaszubowski          <Command.Group heading="Expo documentation">
68*89d2c67fSBartosz Kaszubowski            {expoDocsItems.map(item => (
69*89d2c67fSBartosz Kaszubowski              <ExpoDocsItem item={item} onSelect={dismiss} key={`hit-expo-docs-${item.objectID}`} />
70*89d2c67fSBartosz Kaszubowski            ))}
71*89d2c67fSBartosz Kaszubowski          </Command.Group>
72*89d2c67fSBartosz Kaszubowski        )}
73*89d2c67fSBartosz Kaszubowski        {expoItems.length > 0 && (
74*89d2c67fSBartosz Kaszubowski          <Command.Group heading="Expo dashboard">
75*89d2c67fSBartosz Kaszubowski            {expoItems.map((item: ExpoItemType) => (
76*89d2c67fSBartosz Kaszubowski              <ExpoItem item={item} onSelect={dismiss} key={`hit-expo-${item.url}`} query={query} />
77*89d2c67fSBartosz Kaszubowski            ))}
78*89d2c67fSBartosz Kaszubowski          </Command.Group>
79*89d2c67fSBartosz Kaszubowski        )}
80*89d2c67fSBartosz Kaszubowski        {rnDocsItems.length > 0 && (
81*89d2c67fSBartosz Kaszubowski          <Command.Group heading="React Native documentation">
82*89d2c67fSBartosz Kaszubowski            {rnDocsItems.map(item => (
83*89d2c67fSBartosz Kaszubowski              <RNDocsItem item={item} onSelect={dismiss} key={`hit-rn-docs-${item.objectID}`} />
84*89d2c67fSBartosz Kaszubowski            ))}
85*89d2c67fSBartosz Kaszubowski          </Command.Group>
86*89d2c67fSBartosz Kaszubowski        )}
87*89d2c67fSBartosz Kaszubowski        {directoryItems.length > 0 && (
88*89d2c67fSBartosz Kaszubowski          <Command.Group heading="React Native directory">
89*89d2c67fSBartosz Kaszubowski            {directoryItems.map(item => (
90*89d2c67fSBartosz Kaszubowski              <RNDirectoryItem
91*89d2c67fSBartosz Kaszubowski                item={item}
92*89d2c67fSBartosz Kaszubowski                onSelect={dismiss}
93*89d2c67fSBartosz Kaszubowski                key={`hit-rn-dir-${item.npmPkg}`}
94*89d2c67fSBartosz Kaszubowski                query={query}
95*89d2c67fSBartosz Kaszubowski              />
96*89d2c67fSBartosz Kaszubowski            ))}
97*89d2c67fSBartosz Kaszubowski          </Command.Group>
98*89d2c67fSBartosz Kaszubowski        )}
99*89d2c67fSBartosz Kaszubowski        {totalCount === 0 && (
100*89d2c67fSBartosz Kaszubowski          <Command.Empty>
101*89d2c67fSBartosz Kaszubowski            <CALLOUT theme="secondary">No results found.</CALLOUT>
102*89d2c67fSBartosz Kaszubowski          </Command.Empty>
103*89d2c67fSBartosz Kaszubowski        )}
104*89d2c67fSBartosz Kaszubowski      </Command.List>
105*89d2c67fSBartosz Kaszubowski      <CommandFooter />
106*89d2c67fSBartosz Kaszubowski    </Command.Dialog>
107*89d2c67fSBartosz Kaszubowski  );
108*89d2c67fSBartosz Kaszubowski};
109