File size: 4,979 Bytes
f78ec48
b8c4528
3b780fb
b8c4528
 
 
 
 
f42b4a1
b8c4528
 
3d4392e
b8c4528
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f78ec48
 
 
b8c4528
 
 
 
 
3b780fb
b8c4528
 
3d4392e
b8c4528
 
 
 
3b780fb
 
 
 
 
 
 
 
b8c4528
 
 
 
f78ec48
b8c4528
 
 
f78ec48
b8c4528
 
 
 
 
 
 
 
f78ec48
b8c4528
f78ec48
b8c4528
 
 
 
f78ec48
 
 
 
b8c4528
 
 
 
 
 
 
 
f78ec48
 
b8c4528
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f78ec48
 
b8c4528
 
f78ec48
3b780fb
 
 
 
 
 
b8c4528
 
 
 
 
 
 
 
 
3b780fb
5cb8032
565d074
b8c4528
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import { useRef, useState, useTransition } from "react"
import Link from "next/link"
import { encode, decode } from 'js-base64'
// import throttle from "@jcoreio/async-throttle"
import debounce from "lodash.debounce"
import { GoSearch } from "react-icons/go"

import { useStore } from "@/app/state/useStore"
import { cn } from "@/lib/utils/cn"
import { Input } from "@/components/ui/input"
import { Button } from "@/components/ui/button"
import { getVideos } from "@/app/api/actions/ai-tube-hf/getVideos"

export function SearchInput() {
  const [_pending, startTransition] = useTransition()

  const setSearchAutocompleteQuery = useStore(s => s.setSearchAutocompleteQuery)
  const showAutocompleteBox = useStore(s => s.showAutocompleteBox)
  const setShowAutocompleteBox = useStore(s => s.setShowAutocompleteBox)
  
  const searchAutocompleteResults = useStore(s => s.searchAutocompleteResults)
  const setSearchAutocompleteResults = useStore(s => s.setSearchAutocompleteResults)

  const setSearchQuery = useStore(s => s.setSearchQuery)

  const [searchDraft, setSearchDraft] = useState("")

  const ref = useRef<HTMLInputElement>(null)


  // called when pressing enter or clicking on search
  const debouncedSearch = debounce((query: string) => {
    startTransition(async () => {
      console.log(`searching for "${query}"..`)

      const medias = await getVideos({
        query,
        sortBy: "match",
        maxNbMedias: 8,
        neverThrow: true,
        renewCache: false, // bit of optimization
      })

      console.log(`got ${medias.length} results!`)
      setSearchAutocompleteResults(medias.map(item => ({
        id: item.id,
        title: item.label,
        tags: item.tags,
      })))
      


      // TODO: only close the show autocomplete box if we found something
      // setShowAutocompleteBox(false)
    })
  }, 500)

  // called when pressing enter or clicking on search
  const handleSearch = () => {
    ref.current?.focus()
    setSearchQuery(searchDraft)
    setShowAutocompleteBox(true)
    debouncedSearch(searchDraft)
  }

  return (
    <div className="flex flex-row flex-grow w-[380px] lg:w-[600px]">

      <div className="flex flex-row w-full">
        <Input
          ref={ref}
          placeholder="Search"
          className={cn(
            `bg-neutral-900 text-neutral-200 dark:bg-neutral-900 dark:text-neutral-200`,
            `rounded-l-full rounded-r-none`,
            
            // we increase the line height to better catch the clicks
            `py-0 h-10 leading-7`,

            `border-neutral-700 dark:border-neutral-700 border-r-0`,
    
          )}
          onFocus={() => {
            handleSearch()
          }}
          onChange={(e) => {
            setSearchDraft(e.target.value)
            setShowAutocompleteBox(true)
            // handleSearch()
          }}
          onKeyDown={({ key }) => {
            if (key === 'Enter') {
              handleSearch()
            }
          }}
          value={searchDraft}
        />
        <Button
        className={cn(
          `rounded-l-none rounded-r-full border border-neutral-700 dark:border-neutral-700`,
          `cursor-pointer`,
          `transition-all duration-200 ease-in-out`,
          `text-neutral-200 dark:text-neutral-200 bg-neutral-800 dark:bg-neutral-800 hover:bg-neutral-700 disabled:bg-neutral-900`
          )}
          onClick={() => {
            handleSearch()
            // console.log("submit")
            // setShowAutocompleteBox(false)
            // setSearchDraft("")
          }}
        disabled={false}
      >
        <GoSearch className="w-6 h-6" />
      </Button>
    </div>
    <div
      className={cn(
        `absolute z-50 ml-1`,

        // please keep this in sync with the parent
        `w-[320px] lg:w-[540px]`,

        `text-neutral-200 dark:text-neutral-200 bg-neutral-900 dark:bg-neutral-900`,
        `border border-neutral-800 dark:border-neutral-800`,
        `rounded-xl shadow-2xl`,
        `flex flex-col p-2 space-y-1`,
        
        `transition-all duration-200 ease-in-out`,
        showAutocompleteBox
          ? `opacity-100 scale-100 mt-11 pointer-events-auto`
          : `opacity-0 scale-95 mt-6 pointer-events-none`
      )}
    >
      {searchAutocompleteResults.length === 0 ? <div>Nothing to show, type something and press enter</div> : null}
        {searchAutocompleteResults.map(item => (
          <Link key={item.id} href={
            item.id
            ? `${process.env.NEXT_PUBLIC_DOMAIN}/watch?v=${item.id}`
            : `${process.env.NEXT_PUBLIC_DOMAIN}/latent/watch?p=${encode(JSON.stringify(item))}`
            }>
            <div
              className={cn(
                `dark:hover:bg-neutral-800 hover:bg-neutral-800`,
                `text-sm`,
                `px-3 py-2`,
                `rounded-xl`
              )}

            >
              {item.title}
            </div>
          </Link>
        ))}
    </div>
  </div>
  )
}