2023-11-10 17:17:30 +00:00
|
|
|
"use client"
|
|
|
|
|
2023-11-15 09:16:20 +00:00
|
|
|
import React, {
|
|
|
|
FormEvent,
|
|
|
|
useCallback,
|
|
|
|
useEffect,
|
|
|
|
useRef,
|
|
|
|
useState
|
|
|
|
} from "react"
|
|
|
|
import { ChevronDownIcon, ChevronRightIcon } from "@heroicons/react/24/outline" // Assuming usage of Heroicons for icons
|
2023-11-10 17:17:30 +00:00
|
|
|
import { flushSync } from "react-dom"
|
2023-11-14 15:52:42 +00:00
|
|
|
import Link from "next/link"
|
|
|
|
import { usePathname, useSearchParams, useRouter } from "next/navigation"
|
2023-12-09 15:26:32 +00:00
|
|
|
import { FILTER_TIMES, formatDate, getResults } from "@/utils"
|
2023-12-08 18:29:17 +00:00
|
|
|
import {
|
|
|
|
Select,
|
|
|
|
SelectContent,
|
|
|
|
SelectItem,
|
|
|
|
SelectLabel,
|
|
|
|
SelectTrigger,
|
|
|
|
SelectValue
|
|
|
|
} from "./ui/select"
|
|
|
|
import { SitesCombobox } from "./SitesCombobox"
|
2023-11-10 17:17:30 +00:00
|
|
|
|
2023-12-08 18:29:17 +00:00
|
|
|
type Props = {}
|
2023-11-10 17:17:30 +00:00
|
|
|
|
2023-12-08 18:29:17 +00:00
|
|
|
const SearchBar = ({}: Props) => {
|
2023-11-15 09:16:20 +00:00
|
|
|
const searchParams = useSearchParams()
|
|
|
|
const router = useRouter()
|
|
|
|
const pathname = usePathname()
|
2023-12-08 18:29:17 +00:00
|
|
|
const [query, setQuery] = useState(searchParams?.get("q") ?? "")
|
2023-11-10 17:17:30 +00:00
|
|
|
const inputRef = useRef<HTMLInputElement>()
|
|
|
|
const [isLoading, setIsLoading] = useState(false)
|
|
|
|
const [activeInput, setActiveInput] = useState(true)
|
2023-11-22 11:46:56 +00:00
|
|
|
const [dirtyInput, setDirtyInput] = useState(false)
|
2023-11-15 09:16:20 +00:00
|
|
|
const [results, setResults] = useState<SearchResult[]>([])
|
2023-11-10 17:17:30 +00:00
|
|
|
|
2023-11-15 09:16:20 +00:00
|
|
|
const handleSearch = useCallback(
|
|
|
|
async (event?: FormEvent<HTMLFormElement>) => {
|
|
|
|
event?.preventDefault()
|
|
|
|
setIsLoading(true)
|
2023-12-08 18:29:17 +00:00
|
|
|
const newSearchParams = new URLSearchParams(searchParams ?? undefined)
|
2023-11-14 15:52:42 +00:00
|
|
|
|
2023-11-15 09:16:20 +00:00
|
|
|
if (query) {
|
|
|
|
newSearchParams.set("q", query)
|
|
|
|
} else {
|
|
|
|
newSearchParams.delete("q")
|
|
|
|
}
|
2023-11-14 15:52:42 +00:00
|
|
|
|
2023-11-15 09:16:20 +00:00
|
|
|
router.push(`${pathname}?${newSearchParams}`)
|
2023-11-14 15:52:42 +00:00
|
|
|
|
2023-11-15 09:16:20 +00:00
|
|
|
// Perform search and update results state
|
|
|
|
// Assume fetchResults is a function that fetches search results
|
|
|
|
// const searchResults = await fetchResults(query);
|
|
|
|
// Mock the search results
|
|
|
|
const searchResults = await getResults({ query })
|
|
|
|
|
|
|
|
setResults(searchResults)
|
|
|
|
setIsLoading(false)
|
|
|
|
setActiveInput(false)
|
|
|
|
},
|
|
|
|
[
|
|
|
|
query,
|
|
|
|
setResults,
|
|
|
|
setIsLoading,
|
|
|
|
setActiveInput,
|
|
|
|
searchParams,
|
|
|
|
router,
|
|
|
|
pathname
|
2023-11-10 17:17:30 +00:00
|
|
|
]
|
2023-11-15 09:16:20 +00:00
|
|
|
)
|
2023-11-14 15:52:42 +00:00
|
|
|
|
2023-11-22 11:46:56 +00:00
|
|
|
const isActive = results.length > 0 || dirtyInput
|
|
|
|
|
2023-11-10 17:17:30 +00:00
|
|
|
return (
|
|
|
|
<div
|
2023-11-15 09:35:32 +00:00
|
|
|
className={`w-full mt-8 p-4 border-2 ${
|
2023-11-22 11:46:56 +00:00
|
|
|
isActive ? "border-sky-300 bg-gray-950" : "border-primary"
|
2023-11-10 17:17:30 +00:00
|
|
|
}`}
|
|
|
|
>
|
|
|
|
<form className={`flex items-center text-lg`} onSubmit={handleSearch}>
|
2023-11-22 11:46:56 +00:00
|
|
|
{isLoading || isActive ? (
|
2023-11-10 17:17:30 +00:00
|
|
|
<span className="text-white mr-2">Searching for</span>
|
|
|
|
) : null}
|
|
|
|
{activeInput ? (
|
|
|
|
<fieldset
|
|
|
|
className={`block w-full p-0 h-auto flex-1 overflow-hidden`}
|
|
|
|
>
|
2023-11-22 11:46:56 +00:00
|
|
|
{isActive ? (
|
2023-11-10 17:17:30 +00:00
|
|
|
<span className="text-blue-300 underline-offset-4 underline mr-[-0.5px]">
|
|
|
|
{'"'}
|
|
|
|
</span>
|
|
|
|
) : (
|
|
|
|
""
|
|
|
|
)}
|
|
|
|
<input
|
|
|
|
ref={(element) => {
|
|
|
|
if (element) {
|
|
|
|
inputRef.current = element
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
className={`flex-grow inline bg-transparent text-white placeholder-gray-400 outline-none ring-none ${
|
2023-11-22 11:46:56 +00:00
|
|
|
isActive
|
2023-11-10 17:17:30 +00:00
|
|
|
? `text-blue-300 p-0 underline underline-offset-4`
|
|
|
|
: "w-full p-2"
|
|
|
|
}`}
|
|
|
|
placeholder={
|
2023-11-22 11:59:45 +00:00
|
|
|
!isActive
|
2023-11-10 17:17:30 +00:00
|
|
|
? "Search for web3 news from the community"
|
|
|
|
: undefined
|
|
|
|
}
|
|
|
|
value={query}
|
|
|
|
size={query ? query.length : 1}
|
|
|
|
style={
|
|
|
|
query
|
|
|
|
? {
|
|
|
|
width: `calc(${query.length}ch+2px)`
|
|
|
|
}
|
|
|
|
: undefined
|
|
|
|
}
|
2023-11-22 11:46:56 +00:00
|
|
|
onChange={(e) => {
|
2023-12-08 18:29:17 +00:00
|
|
|
if (!dirtyInput) {
|
|
|
|
setDirtyInput(true)
|
2023-11-22 11:46:56 +00:00
|
|
|
}
|
|
|
|
setQuery(e.target.value)
|
|
|
|
}}
|
2023-11-10 17:17:30 +00:00
|
|
|
/>
|
2023-11-22 11:46:56 +00:00
|
|
|
{isActive ? (
|
2023-11-10 17:17:30 +00:00
|
|
|
<span className="text-blue-300 underline-offset-4 underline ml-[-5.5px]">
|
|
|
|
{'"'}
|
|
|
|
</span>
|
|
|
|
) : (
|
|
|
|
""
|
|
|
|
)}
|
|
|
|
</fieldset>
|
|
|
|
) : (
|
|
|
|
<span
|
|
|
|
className="block w-full flex-1 text-blue-300"
|
|
|
|
onClick={() => {
|
2023-11-15 09:16:20 +00:00
|
|
|
flushSync(() => {
|
|
|
|
setActiveInput(true)
|
|
|
|
})
|
|
|
|
inputRef.current?.focus()
|
2023-11-10 17:17:30 +00:00
|
|
|
}}
|
|
|
|
>
|
|
|
|
{'"'}
|
|
|
|
{query}
|
|
|
|
{'"'}
|
|
|
|
</span>
|
|
|
|
)}
|
|
|
|
{isLoading ? (
|
|
|
|
// Shadcn Loading component placeholder
|
|
|
|
<LoadingComponent />
|
|
|
|
) : (
|
2023-12-08 18:29:17 +00:00
|
|
|
<div className="justify-self-end min-w-[220px] flex justify-end gap-2">
|
2023-11-10 17:17:30 +00:00
|
|
|
{/* Dropdown component should be here */}
|
2023-12-08 18:29:17 +00:00
|
|
|
<SitesCombobox />
|
2023-11-10 17:17:30 +00:00
|
|
|
{/* Dropdown component should be here */}
|
2023-12-08 18:29:17 +00:00
|
|
|
<Select defaultValue={'0'}>
|
|
|
|
<SelectTrigger className="hover:bg-muted w-auto">
|
|
|
|
<SelectValue placeholder="Time ago"/>
|
|
|
|
</SelectTrigger>
|
|
|
|
<SelectContent>
|
|
|
|
{FILTER_TIMES.map((v) => (
|
|
|
|
<SelectItem value={String(v.value)} key={`FilteTimeSelectItem_${v.value}`}>{v.label}</SelectItem>
|
|
|
|
))}
|
|
|
|
</SelectContent>
|
|
|
|
</Select>
|
2023-11-10 17:17:30 +00:00
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</form>
|
|
|
|
|
|
|
|
{results.length > 0 && (
|
|
|
|
<>
|
|
|
|
<hr className="my-4 border-1" />
|
|
|
|
{results.map((item) => (
|
2023-11-15 09:16:20 +00:00
|
|
|
<Link
|
|
|
|
href={`/article/${item.slug}`}
|
2023-11-10 17:17:30 +00:00
|
|
|
key={item.id}
|
|
|
|
className="flex cursor-pointer flex-row items-center space-x-5 my-2 py-2 px-4 hover:bg-gray-800 rounded-md"
|
|
|
|
>
|
|
|
|
<span className="text-sm text-gray-400">
|
|
|
|
{formatDate(item.timestamp)}
|
|
|
|
</span>
|
|
|
|
<h3 className="text-md font-semibold text-white">{item.title}</h3>
|
2023-11-15 09:16:20 +00:00
|
|
|
</Link>
|
2023-11-10 17:17:30 +00:00
|
|
|
))}
|
2023-11-14 15:52:42 +00:00
|
|
|
<Link href={`/search?q=${encodeURIComponent(query)}`}>
|
|
|
|
<button className="mt-4 flex justify-center items-center bg-secondary w-full py-7 text-white hover:bg-teal-800 transition-colors">
|
2023-11-15 09:16:20 +00:00
|
|
|
{results.length}+ search results for{" "}
|
|
|
|
<span className="text-blue-300 ml-1">{query}</span>
|
2023-11-14 15:52:42 +00:00
|
|
|
<ChevronRightIcon className="w-5 h-5 inline ml-2 mt-[1px]" />
|
2023-11-10 17:17:30 +00:00
|
|
|
</button>
|
2023-11-14 15:52:42 +00:00
|
|
|
</Link>
|
2023-11-10 17:17:30 +00:00
|
|
|
</>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Placeholder components for Shadcn
|
|
|
|
const LoadingComponent = () => {
|
|
|
|
// Replace with actual Shadcn Loading component
|
|
|
|
return <div>Loading...</div>
|
|
|
|
}
|
|
|
|
|
|
|
|
export default SearchBar
|