import { IDBPDatabase } from "idb"; import { ChatStore } from "./app"; import { StateUpdater, useRef, useState } from "preact/hooks"; interface ChatStoreSearchResult { key: IDBValidKey; cs: ChatStore; query: string; preview: string; } export default function Search(props: { db: Promise>; setSelectedChatIndex: StateUpdater; chatStore: ChatStore; setShow: (show: boolean) => void; }) { const [searchResult, setSearchResult] = useState([]); const [searching, setSearching] = useState(false); const [searchingNow, setSearchingNow] = useState(0); const [pageIndex, setPageIndex] = useState(0); const searchAbortRef = useRef(null); return (
props.setShow(false)} className="left-0 top-0 overflow-scroll flex justify-center absolute w-screen h-full bg-black bg-opacity-50 z-10" >
{ event.stopPropagation(); }} className="m-2 p-2 bg-white rounded-lg h-fit w-2/3 z-20" >
Search

{ const query = event.target.value.trim(); if (!query) { setSearchResult([]); return; } // abort previous search if (searchAbortRef.current) { searchAbortRef.current.abort(); } // Create a new AbortController for the new operation const abortController = new AbortController(); searchAbortRef.current = abortController; const signal = abortController.signal; setSearching(true); const db = await props.db; const resultKeys = await db.getAllKeys("chatgpt-api-web"); const result: ChatStoreSearchResult[] = []; for (const key of resultKeys) { // abort the operation if the signal is set if (signal.aborted) { return; } const now = Math.floor( (result.length / resultKeys.length) * 100 ); if (now !== searchingNow) setSearchingNow(now); const value: ChatStore = await db.get("chatgpt-api-web", key); const content = value.contents_for_index.join(" "); if (content.includes(query)) { const beginIndex: number = content.indexOf(query); const preview = content.slice( Math.max(0, beginIndex - 100), Math.min(content.length, beginIndex + 239) ); result.push({ key, cs: value, query: query, preview: preview, }); } } // sort by key desc result.sort((a, b) => { if (a.key < b.key) { return 1; } if (a.key > b.key) { return -1; } return 0; }); console.log(result); setPageIndex(0); setSearchResult(result); setSearching(false); }} />
{searching &&
Searching {searchingNow}%...
} {searchResult.length > 0 && (
Page: {pageIndex + 1} / {Math.floor(searchResult.length / 10) + 1}
)}
{searchResult .slice(pageIndex * 10, (pageIndex + 1) * 10) .map((result: ChatStoreSearchResult) => { return (
{ props.setSelectedChatIndex(parseInt(result.key.toString())); props.setShow(false); }} >
{result.key}
{result.preview}
); })}
); }