import React, { useEffect, useRef } from 'react' import { FlatList, StyleSheet, View } from 'react-native' import { Banner, Spinner, Text, TextButton } from '../../../components' import { useSearchResults } from '../../../hooks/use_search_results' import type { ConversationSearchResultResource, MessageSearchResultResource, SearchResultResource, } from '../../../types/resources/search_result' import { ConversationResults } from './conversation_results' import { MessageResultRow } from './message_result_row' import { ResultsSection } from './results_section' import { NoResultsBlankState } from './search_blank_states' const SEARCH_ERROR_MESSAGE = "Couldn't search right now. Try again." export interface SearchResultCounts { conversationResultCount: number messageResultCount: number } interface SearchResultsListProps { term: string onSelect: (result: SearchResultResource, position: number) => void onSettled: (term: string, counts: SearchResultCounts) => void } export function SearchResultsList({ term, onSelect, onSettled }: SearchResultsListProps) { const { results, failedTypes, isPending, isError, isFetchNextPageError, loadMore, hasNextPage, isFetchingNextPage, } = useSearchResults(term) const conversationResults = results.filter(isConversationResult) const messageResults = results.filter(isMessageResult) const settled = !isPending && !isError const settledTermRef = useRef(undefined) useEffect(() => { if (!settled || settledTermRef.current === term) return settledTermRef.current = term onSettled(term, { conversationResultCount: conversationResults.length, messageResultCount: messageResults.length, }) }, [settled, term, conversationResults.length, messageResults.length, onSettled]) return ( item.id} keyboardShouldPersistTaps="handled" keyboardDismissMode="on-drag" contentContainerStyle={styles.content} renderItem={({ item, index }) => ( onSelect(item, index)} /> )} ListHeaderComponent={ <> {conversationResults.length > 0 && ( )} {failedTypes.includes('conversation') && } {messageResults.length > 0 && } } ListEmptyComponent={ } ListFooterComponent={ <> {isFetchingNextPage && ( )} {isFetchNextPageError && !isFetchingNextPage && ( Try again )} {failedTypes.includes('message') && } } onEndReached={() => { if (hasNextPage && !isFetchingNextPage && !isFetchNextPageError) loadMore() }} onEndReachedThreshold={0.5} /> ) } interface ListEmptyProps { term: string isError: boolean isPending: boolean noResults: boolean } function ListEmpty({ term, isError, isPending, noResults }: ListEmptyProps) { if (isError) { return } if (isPending) { return ( ) } if (noResults) return return null } function SectionFailure({ type }: { type: SearchResultResource['type'] }) { return ( Couldn’t load {type}s. ) } function isConversationResult( result: SearchResultResource ): result is ConversationSearchResultResource { return result.type === 'conversation' } function isMessageResult(result: SearchResultResource): result is MessageSearchResultResource { return result.type === 'message' } const styles = StyleSheet.create({ content: { flexGrow: 1, }, banner: { margin: 16, }, loading: { alignItems: 'center', paddingVertical: 16, }, retry: { alignSelf: 'center', paddingVertical: 16, }, sectionFailure: { fontSize: 14, paddingHorizontal: 16, paddingVertical: 8, }, })