import AsyncStorage from '@react-native-community/async-storage'; import {useFocusEffect} from '@react-navigation/native'; import React, {useEffect, useState} from 'react'; import {Keyboard, ScrollView, StatusBar, StyleSheet} from 'react-native'; import Animated, {Easing, timing} from 'react-native-reanimated'; import {SafeAreaView} from 'react-native-safe-area-context'; import {useDispatch, useSelector} from 'react-redux'; import { RecentSearches, SearchBar, SearchCategories, SearchResultList, SearchResultsBackground, TabsGradient, } from '../../components'; import {SEARCH_ENDPOINT, TAGG_LIGHT_BLUE} from '../../constants'; import {loadSearchResults} from '../../services'; import {resetScreenType} from '../../store/actions'; import {RootState} from '../../store/rootReducer'; import { CategoryPreviewType, ProfilePreviewType, ScreenType, SearchCategoryType, } from '../../types'; import { getRecentlySearchedCategories, getRecentlySearchedUsers, normalize, SCREEN_HEIGHT, SCREEN_WIDTH, } from '../../utils'; /** * Search Screen for user recommendations and a search * tool to allow user to find other users */ const SearchScreen: React.FC = () => { const {recentSearches} = useSelector((state: RootState) => state.taggUsers); const { profile: {university = ''}, } = useSelector((state: RootState) => state.user); const [query, setQuery] = useState(''); const [results, setResults] = useState | undefined>(undefined); const [recents, setRecents] = useState>( recentSearches ?? [], ); const [recentCategories, setRecentCategories] = useState< CategoryPreviewType[] >([]); const [searching, setSearching] = useState(false); const top = Animated.useValue(-SCREEN_HEIGHT); const defaultButtons: SearchCategoryType[] = [21, 22, 23, 24].map((year) => ({ id: -1, name: `${university.split(' ')[0]} '${year}`, category: university, })); const [keyboardVisible, setKeyboardVisible] = React.useState( 'keyboardVisible', ); useEffect(() => { const showKeyboard = () => setKeyboardVisible('keyboardVisibleTrue'); Keyboard.addListener('keyboardWillShow', showKeyboard); return () => Keyboard.removeListener('keyboardWillShow', showKeyboard); }, []); useEffect(() => { const hideKeyboard = () => setKeyboardVisible('keyboardVisibleFalse'); Keyboard.addListener('keyboardWillHide', hideKeyboard); return () => Keyboard.removeListener('keyboardWillHide', hideKeyboard); }, []); const dispatch = useDispatch(); /* * Main handler for changes in query. */ useEffect(() => { if (!searching) { return; } if (query.length < 3) { loadRecentlySearched().then(() => setResults(undefined)); return; } (async () => { const searchResults = await loadSearchResults( `${SEARCH_ENDPOINT}?query=${query}`, ); if (query.length > 2) { const sanitizedResult = [ { title: 'badges', data: searchResults?.badges, }, { title: 'categories', data: searchResults?.categories, }, { title: 'users', data: searchResults?.users, }, ]; setResults(sanitizedResult); } else { setResults(undefined); } })(); }, [query]); /** * Code under useFocusEffect gets executed every time the screen comes under focus / is being viewed by the user. * This is done to reset the users stored in our store for the Search screen. * Read more here : https://reactnavigation.org/docs/function-after-focusing-screen/ */ useFocusEffect(() => { dispatch(resetScreenType(ScreenType.Search)); }); // when searching state changes, run animation and reset query accordingly useEffect(() => { if (searching) { loadRecentlySearched().then(() => { timing(top, topInConfig).start(); }); } else { setQuery(''); handleBlur(); timing(top, topOutConfig).start(() => setResults(undefined)); } }, [searching]); // loads recent searches (users & categories) from AsyncStorage const loadRecentlySearched = async () => { return Promise.all([ getRecentlySearchedUsers(), getRecentlySearchedCategories(), ]).then( ([users, categories]: [ProfilePreviewType[], CategoryPreviewType[]]) => { setRecents(users); setRecentCategories(categories); }, ); }; const clearRecentlySearched = async () => { try { AsyncStorage.removeItem('@recently_searched_users'); AsyncStorage.removeItem('@recently_searched_categories'); loadRecentlySearched(); } catch (e) { console.log(e); } }; const topInConfig = { duration: 180, toValue: 0, easing: Easing.bezier(0.31, 0.14, 0.66, 0.82), }; const topOutConfig = { duration: 180, toValue: -SCREEN_HEIGHT, easing: Easing.inOut(Easing.ease), }; const handleFocus = () => { setSearching(true); }; const handleBlur = () => { Keyboard.dismiss(); }; const handleCancel = () => { setSearching(false); }; return ( {results === undefined && recents.length + recentCategories.length !== 0 ? ( ) : ( )} ); }; const styles = StyleSheet.create({ screenContainer: { paddingTop: 15, backgroundColor: '#fff', }, contentContainer: { height: SCREEN_HEIGHT, paddingTop: '2%', paddingBottom: SCREEN_HEIGHT / 3, paddingHorizontal: '3%', }, header: { marginVertical: 20, zIndex: 1, }, recentsHeaderContainer: { flexDirection: 'row', }, recentsHeader: { fontSize: 17, fontWeight: 'bold', flexGrow: 1, }, clear: { fontSize: normalize(17), fontWeight: 'bold', color: TAGG_LIGHT_BLUE, }, image: { width: SCREEN_WIDTH, height: SCREEN_WIDTH, }, textContainer: { marginTop: '10%', }, headerText: { color: '#fff', fontSize: normalize(32), fontWeight: '600', textAlign: 'center', marginBottom: '4%', marginHorizontal: '10%', }, subtext: { color: '#fff', fontSize: normalize(16), fontWeight: '600', textAlign: 'center', marginHorizontal: '10%', }, cancelButton: { position: 'absolute', height: '100%', justifyContent: 'center', paddingHorizontal: 5, }, cancelText: { color: '#818181', fontWeight: '600', }, }); export default SearchScreen;