Add spinner and some other minor changes

This commit is contained in:
bkellam 2024-08-24 20:58:31 -07:00
parent 90b6256a4e
commit 0ae2030cb1

View file

@ -3,11 +3,12 @@
import Image from "next/image"; import Image from "next/image";
import logo from "../../public/sb_logo_large_3.png" import logo from "../../public/sb_logo_large_3.png"
import { Input } from "@/components/ui/input" import { Input } from "@/components/ui/input"
import { useEffect, useRef, useState } from "react"; import { useEffect, useState } from "react";
import { useDebouncedCallback } from 'use-debounce'; import { useDebouncedCallback } from 'use-debounce';
import { Separator } from "@/components/ui/separator" import { Separator } from "@/components/ui/separator"
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useNonEmptyQueryParam } from "@/hooks/useNonEmptyQueryParam"; import { useNonEmptyQueryParam } from "@/hooks/useNonEmptyQueryParam";
import { SymbolIcon } from "@radix-ui/react-icons";
interface ZoekMatch { interface ZoekMatch {
URL: string, URL: string,
@ -31,10 +32,14 @@ interface ZoekFileMatch {
interface ZoekResult { interface ZoekResult {
QueryStr: string, QueryStr: string,
FileMatches: ZoekFileMatch[] | null, FileMatches: ZoekFileMatch[] | null,
Stats: {
// Duration in nanoseconds
Duration: number,
}
} }
interface ZoekSearchResponse { interface ZoekSearchResponse {
result: ZoekResult; result: ZoekResult,
} }
export default function Home() { export default function Home() {
@ -46,15 +51,11 @@ export default function Home() {
const [numResults, _setNumResults] = useState(defaultNumResults && !isNaN(Number(defaultNumResults)) ? Number(defaultNumResults) : 100); const [numResults, _setNumResults] = useState(defaultNumResults && !isNaN(Number(defaultNumResults)) ? Number(defaultNumResults) : 100);
const [fileMatches, setFileMatches] = useState<ZoekFileMatch[]>([]); const [fileMatches, setFileMatches] = useState<ZoekFileMatch[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [searchDurationMs, setSearchDurationMs] = useState(0);
/** // @todo: We need to be able to handle the case when the user navigates backwards / forwards.
* @note : when the user navigates backwards/forwards, the defaultQuery // Currently we do not re-query.
* will update, but the query state will not. This effect keeps things in
* sync for that scenario.
*/
useEffect(() => {
setQuery(defaultQuery);
}, [defaultQuery]);
return ( return (
<main className="flex h-screen flex-col"> <main className="flex h-screen flex-col">
@ -68,15 +69,23 @@ export default function Home() {
query={query} query={query}
numResults={numResults} numResults={numResults}
onQueryChange={(query) => setQuery(query)} onQueryChange={(query) => setQuery(query)}
onLoadingChange={(isLoading) => setIsLoading(isLoading)}
onSearchResult={(result) => { onSearchResult={(result) => {
setFileMatches(result?.FileMatches ?? []); if (result) {
setFileMatches(result.FileMatches ?? []);
setSearchDurationMs(Math.round(result.Stats.Duration / 1000000));
}
router.push(`?query=${query}&numResults=${numResults}`); router.push(`?query=${query}&numResults=${numResults}`);
}} }}
/> />
{isLoading && (
<SymbolIcon className="h-4 w-4 animate-spin" />
)}
</div> </div>
<Separator /> <Separator />
<div className="bg-accent p-2"> <div className="bg-accent p-2">
<p className="text-sm font-medium">Results for: {fileMatches.length} files</p> <p className="text-sm font-medium">Results for: {fileMatches.length} files in {searchDurationMs} ms</p>
</div> </div>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
{fileMatches.map((match, index) => ( {fileMatches.map((match, index) => (
@ -90,6 +99,7 @@ export default function Home() {
interface SearchBarProps { interface SearchBarProps {
query: string; query: string;
numResults: number; numResults: number;
onLoadingChange: (isLoading: boolean) => void;
onQueryChange: (query: string) => void; onQueryChange: (query: string) => void;
onSearchResult: (result?: ZoekResult) => void, onSearchResult: (result?: ZoekResult) => void,
} }
@ -97,17 +107,21 @@ interface SearchBarProps {
const SearchBar = ({ const SearchBar = ({
query, query,
numResults, numResults,
onLoadingChange,
onQueryChange, onQueryChange,
onSearchResult, onSearchResult,
}: SearchBarProps) => { }: SearchBarProps) => {
const SEARCH_DEBOUNCE_MS = 200; const SEARCH_DEBOUNCE_MS = 200;
// @todo : we should probably be cancelling any running requests
const search = useDebouncedCallback((query: string) => { const search = useDebouncedCallback((query: string) => {
if (query === "") { if (query === "") {
onSearchResult(undefined); onSearchResult(undefined);
return; return;
} }
console.log('making query...'); console.log('making query...');
onLoadingChange(true);
fetch(`http://localhost:3000/zoekt/search?query=${query}&numResults=${numResults}`) fetch(`http://localhost:3000/zoekt/search?query=${query}&numResults=${numResults}`)
.then(response => response.json()) .then(response => response.json())
.then(({ data }: { data: ZoekSearchResponse }) => { .then(({ data }: { data: ZoekSearchResponse }) => {
@ -118,7 +132,8 @@ const SearchBar = ({
console.error('Error:', error); console.error('Error:', error);
}).finally(() => { }).finally(() => {
console.log('done making query'); console.log('done making query');
}) onLoadingChange(false);
});
}, SEARCH_DEBOUNCE_MS); }, SEARCH_DEBOUNCE_MS);
useEffect(() => { useEffect(() => {