import React, {useEffect, useState} from 'react'; import { Keyboard, LayoutChangeEvent, NativeSyntheticEvent, StyleSheet, Text, TextInput, TextInputProps, TextInputSubmitEditingEventData, TouchableOpacity, View, ViewStyle, } from 'react-native'; import Animated, {useAnimatedStyle} from 'react-native-reanimated'; import Icon from 'react-native-vector-icons/Feather'; import {useSelector} from 'react-redux'; import {RootState} from '../../store/rootReducer'; import {getSearchSuggestions, normalize} from '../../utils'; const AnimatedIcon = Animated.createAnimatedComponent(Icon); interface SearchBarProps extends TextInputProps { onCancel?: () => void; animationProgress?: Animated.SharedValue; searching?: boolean; onLayout?: (e: LayoutChangeEvent) => void; } const SearchBar: React.FC = ({ onFocus, onBlur, onChangeText, value, onCancel, searching, animationProgress, onLayout, }) => { const handleSubmit = ( e: NativeSyntheticEvent, ) => { e.preventDefault(); Keyboard.dismiss(); }; const {university} = useSelector((state: RootState) => state.user.profile); const DEFAULT_PLACEHOLDER: string = 'Search'; // the list of suggestions to cycle through. TODO: get this from the backend const SEARCH_SUGGESTIONS: string[] = getSearchSuggestions(university); /* * index & id of current placeholder, used in selecting next placeholder. -1 * indicates DEFAULT_PLACEHOLDER. TODO: make it appear more random by tracking * last 3-5 ids & use longer list of placeholders */ const [placeholderId, setPlaceholderId] = useState(-1); // the current placeholder const [placeholder, setPlaceholder] = useState(DEFAULT_PLACEHOLDER); /* * Utility function that generates a random integer in [0, xCeil). * * @param xCeil - the exclusive ceiling (getRandomInt(2) => 0 or 1, not 2) * @returns a random integer in the range [0, xCeil) */ const getRandomInt = (xCeil: number): number => { return Math.floor(Math.random() * Math.floor(xCeil)); }; /* * Handler for `placeholderChangeInterval` that sets the next placeholderId. */ const updatePlaceholder = () => { let nextId: number = getRandomInt(SEARCH_SUGGESTIONS.length); while (nextId === placeholderId) { nextId = getRandomInt(SEARCH_SUGGESTIONS.length); } // TODO: FIGURE OUT WHY CHANGES IN placeholderId ARE NOT REFLECTED HERE // my thought: the value is set when the function is defined, so it keeps // its inital value of -1 forever. setPlaceholderId(nextId); }; /* * Update `placeholder` when `placeholderId` is updated by the interval handler. */ useEffect(() => { if (placeholderId === -1) { setPlaceholder(DEFAULT_PLACEHOLDER); return; } setPlaceholder( DEFAULT_PLACEHOLDER.concat(` '${SEARCH_SUGGESTIONS[placeholderId]}'`), ); }, [placeholderId]); /* * Sets the interval when the user begins searching and clears it when the user is done. */ useEffect(() => { if (!searching) { return; } updatePlaceholder(); const updateInterval = setInterval(() => { updatePlaceholder(); }, 4000); return () => { clearInterval(updateInterval); setPlaceholderId(-1); }; }, [searching]); /* * On-search marginRight style ("cancel" button slides and fades in). */ const animatedStyles = useAnimatedStyle(() => ({ marginRight: (animationProgress ? animationProgress.value : 0) * 58, opacity: animationProgress ? animationProgress.value : 0, })); return ( {onCancel && ( Cancel )} ); }; const styles = StyleSheet.create({ container: { height: 40, paddingHorizontal: 20, flexDirection: 'row', zIndex: 2, }, inputContainer: { flexGrow: 1, flexDirection: 'row', alignItems: 'center', paddingHorizontal: 8, borderRadius: 20, backgroundColor: '#F0F0F0', }, searchIcon: { marginRight: 8, }, input: { flex: 1, fontSize: normalize(15), color: '#000', letterSpacing: 0.5, }, cancelButton: { height: '100%', position: 'absolute', justifyContent: 'center', paddingHorizontal: 8, }, cancelText: { color: '#818181', fontWeight: '500', }, }); export default SearchBar;