import { useMemo, useRef, useState } from "react"; import { FlatList, Keyboard, Pressable, TextInput, View } from "react-native"; import { router, Stack } from "expo-router"; import BookmarkList from "@/components/bookmarks/BookmarkList"; import FullPageError from "@/components/FullPageError"; import CustomSafeAreaView from "@/components/ui/CustomSafeAreaView"; import FullPageSpinner from "@/components/ui/FullPageSpinner"; import { SearchInput } from "@/components/ui/SearchInput"; import { Text } from "@/components/ui/Text"; import { api } from "@/lib/trpc"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { keepPreviousData } from "@tanstack/react-query"; import { useSearchHistory } from "@karakeep/shared-react/hooks/search-history"; import { useDebounce } from "@karakeep/shared-react/hooks/use-debounce"; const MAX_DISPLAY_SUGGESTIONS = 5; export default function Search() { const [search, setSearch] = useState(""); const query = useDebounce(search, 10); const inputRef = useRef(null); const [isInputFocused, setIsInputFocused] = useState(true); const { history, addTerm, clearHistory } = useSearchHistory({ getItem: (k: string) => AsyncStorage.getItem(k), setItem: (k: string, v: string) => AsyncStorage.setItem(k, v), removeItem: (k: string) => AsyncStorage.removeItem(k), }); const onRefresh = api.useUtils().bookmarks.searchBookmarks.invalidate; const { data, error, refetch, isPending, isFetching, fetchNextPage, isFetchingNextPage, } = api.bookmarks.searchBookmarks.useInfiniteQuery( { text: query }, { placeholderData: keepPreviousData, gcTime: 0, initialCursor: null, getNextPageParam: (lastPage) => lastPage.nextCursor, }, ); const filteredHistory = useMemo(() => { if (search.trim().length === 0) { // Show recent items when not typing return history.slice(0, MAX_DISPLAY_SUGGESTIONS); } // Show filtered items when typing return history .filter((item) => item.toLowerCase().includes(search.toLowerCase())) .slice(0, MAX_DISPLAY_SUGGESTIONS); }, [search, history]); if (error) { return refetch()} />; } const handleSearchSubmit = (searchTerm: string) => { const term = searchTerm.trim(); if (term.length > 0) { addTerm(term); setSearch(term); } inputRef.current?.blur(); Keyboard.dismiss(); }; const renderHistoryItem = ({ item }: { item: string }) => ( handleSearchSubmit(item)} className="border-b border-gray-200 p-3" > {item} ); const handleOnFocus = () => { setIsInputFocused(true); }; const handleOnBlur = () => { setIsInputFocused(false); if (search.trim().length > 0) { addTerm(search); } }; return ( handleSearchSubmit(search)} returnKeyType="search" autoFocus autoCapitalize="none" onCancel={router.back} /> {isInputFocused ? ( `${item}-${index}`} ListHeaderComponent={ Recent Searches {history.length > 0 && ( Clear )} } ListEmptyComponent={ No matching searches. } keyboardShouldPersistTaps="handled" /> ) : isFetching && query.length > 0 ? ( ) : data && query.length > 0 ? ( p.bookmarks)} fetchNextPage={fetchNextPage} isFetchingNextPage={isFetchingNextPage} onRefresh={onRefresh} isRefreshing={isPending} /> ) : ( )} ); }