From 7596b69482914569cbb4bb5f287bbc0a72d74133 Mon Sep 17 00:00:00 2001 From: Justin Shillingford Date: Wed, 19 Aug 2020 13:27:22 -0400 Subject: [TMA-161] Recently Searched Users (#34) * Basic AsyncStorage code * Basic implementation complete Need to fix re-rendering bug * Removed errant comment * Fixed bug for rerendering upon addition to recents Need to fix bug for rerendering upon clearing * Fixed rerendering bug for clear * Only present recents header if users in recents * Lint cleaning * Basic AsyncStorage code * Basic implementation complete Need to fix re-rendering bug * Removed errant comment * Fixed bug for rerendering upon addition to recents Need to fix bug for rerendering upon clearing * Fixed rerendering bug for clear * Only present recents header if users in recents * Lint cleaning * Added comments for a function * Updated conditional presentation to use ternary * Created component for List Section Headers * Lint cleaning * Converted component to be for Recent Searches As opposed to just the list header --- src/components/common/index.ts | 1 + src/components/search/RecentSearches.tsx | 52 +++++++++++++++++++++++++++++ src/components/search/SearchResult.tsx | 53 +++++++++++++++++++++++++++++- src/routes/authentication/AuthProvider.tsx | 25 +++++++++++++- src/screens/search/SearchScreen.tsx | 53 ++++++++++++++++++++++++++++-- 5 files changed, 180 insertions(+), 4 deletions(-) create mode 100644 src/components/search/RecentSearches.tsx (limited to 'src') diff --git a/src/components/common/index.ts b/src/components/common/index.ts index 63a7b9c2..c9c4f27a 100644 --- a/src/components/common/index.ts +++ b/src/components/common/index.ts @@ -6,3 +6,4 @@ export {default as GradientBackground} from './GradientBackground'; export {default as Post} from './post'; export {default as SocialIcon} from './SocialIcon'; export {default as TabsGradient} from './TabsGradient'; +export {default as RecentSearches} from '../search/RecentSearches'; diff --git a/src/components/search/RecentSearches.tsx b/src/components/search/RecentSearches.tsx new file mode 100644 index 00000000..a5c08984 --- /dev/null +++ b/src/components/search/RecentSearches.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { + View, + Text, + TouchableOpacity, + StyleSheet, + TouchableOpacityProps, +} from 'react-native'; +import {ProfilePreviewType} from 'src/types'; +import SearchResults from './SearchResults'; + +interface RecentSearchesProps extends TouchableOpacityProps { + sectionTitle: string; + sectionButtonTitle: string; + recents: Array; +} +/** + * An image component that returns the of the icon for a specific social media platform. + */ +const RecentSearches: React.FC = (props) => { + return ( + <> + + {props.sectionTitle} + {props.sectionButtonTitle && ( + + Clear all + + )} + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flexDirection: 'row', + }, + title: { + fontSize: 17, + fontWeight: 'bold', + flexGrow: 1, + }, + clear: { + fontSize: 17, + fontWeight: 'bold', + color: '#698DD3', + }, +}); + +export default RecentSearches; diff --git a/src/components/search/SearchResult.tsx b/src/components/search/SearchResult.tsx index 60c22d41..e65be1f4 100644 --- a/src/components/search/SearchResult.tsx +++ b/src/components/search/SearchResult.tsx @@ -9,6 +9,7 @@ import { TouchableOpacity, } from 'react-native'; import RNFetchBlob from 'rn-fetch-blob'; +import AsyncStorage from '@react-native-community/async-storage'; import {AVATAR_PHOTO_ENDPOINT} from '../../constants'; interface SearchResultProps extends ViewProps { @@ -48,8 +49,58 @@ const SearchResult: React.FC = ({ }; }, [id]); + /** + * Adds a searched user to the recently searched cache if they're tapped on. + * Cache maintains 10 recently searched users, popping off the oldest one if + * needed to make space. + */ + const addToRecentlyStored = async () => { + let user: ProfilePreviewType = { + id, + username, + first_name, + last_name, + }; + try { + const jsonValue = await AsyncStorage.getItem('@recently_searched_users'); + let recentlySearchedList = + jsonValue != null ? JSON.parse(jsonValue) : null; + if (recentlySearchedList) { + if (recentlySearchedList.length > 0) { + if ( + recentlySearchedList.some( + (saved_user: ProfilePreviewType) => saved_user.id === id, + ) + ) { + console.log('User already in recently searched.'); + } else { + if (recentlySearchedList.length >= 10) { + recentlySearchedList.pop(); + } + recentlySearchedList.unshift(user); + } + } + } else { + recentlySearchedList = [user]; + } + try { + let recentlySearchedListString = JSON.stringify(recentlySearchedList); + await AsyncStorage.setItem( + '@recently_searched_users', + recentlySearchedListString, + ); + } catch (e) { + console.log(e); + } + } catch (e) { + console.log(e); + } + }; + return ( - + ; + recentSearches: Array; } const NO_USER: UserType = { userId: '', @@ -35,6 +42,7 @@ export const AuthContext = createContext({ avatar: null, cover: null, instaPosts: [], + recentSearches: [], }); /** @@ -46,6 +54,9 @@ const AuthProvider: React.FC = ({children}) => { const [avatar, setAvatar] = useState(null); const [cover, setCover] = useState(null); const [instaPosts, setInstaPosts] = useState>([]); + const [recentSearches, setRecentSearches] = useState< + Array + >([]); const {userId} = user; useEffect(() => { @@ -115,10 +126,21 @@ const AuthProvider: React.FC = ({children}) => { console.log(error); } }; + const loadRecentlySearchedUsers = async () => { + try { + const asyncCache = await AsyncStorage.getItem( + '@recently_searched_users', + ); + asyncCache != null ? setRecentSearches(JSON.parse(asyncCache)) : null; + } catch (e) { + console.log(e); + } + }; loadProfileInfo(); loadAvatar(); loadCover(); loadInstaPosts(); + loadRecentlySearchedUsers(); }, [userId]); return ( @@ -135,6 +157,7 @@ const AuthProvider: React.FC = ({children}) => { logout: () => { setUser(NO_USER); }, + recentSearches, }}> {children} diff --git a/src/screens/search/SearchScreen.tsx b/src/screens/search/SearchScreen.tsx index 94b9ab41..d85c0a90 100644 --- a/src/screens/search/SearchScreen.tsx +++ b/src/screens/search/SearchScreen.tsx @@ -8,11 +8,14 @@ import { SearchResultsBackground, SearchResults, TabsGradient, + RecentSearches, } from '../../components'; import {SCREEN_HEIGHT, StatusBarHeight} from '../../utils'; import Animated, {Easing, timing} from 'react-native-reanimated'; +import AsyncStorage from '@react-native-community/async-storage'; import {ProfilePreviewType} from '../../types'; import {SEARCH_ENDPOINT} from '../../constants'; +import {AuthContext} from '../../routes/authentication'; const {Value} = Animated; /** @@ -22,8 +25,12 @@ const {Value} = Animated; const top: Animated.Value = new Value(-SCREEN_HEIGHT); const SearchScreen: React.FC = () => { + const {recentSearches} = React.useContext(AuthContext); const [query, setQuery] = useState(''); const [results, setResults] = useState>([]); + const [recents, setRecents] = useState>( + recentSearches, + ); useEffect(() => { if (query.length < 3) { setResults([]); @@ -66,6 +73,26 @@ const SearchScreen: React.FC = () => { }; timing(top, topOutConfig).start(); }; + const loadRecentlySearchedUsers = async () => { + try { + const asyncCache = await AsyncStorage.getItem('@recently_searched_users'); + asyncCache != null ? setRecents(JSON.parse(asyncCache)) : setRecents([]); + } catch (e) { + console.log(e); + } + }; + const clearRecentlySearched = async () => { + try { + await AsyncStorage.removeItem('@recently_searched_users'); + loadRecentlySearchedUsers(); + } catch (e) { + console.log(e); + } + }; + const handleUpdate = async (val: string) => { + setQuery(val); + loadRecentlySearchedUsers(); + }; return ( @@ -79,7 +106,7 @@ const SearchScreen: React.FC = () => { { /> - + {results.length === 0 && recents.length !== 0 ? ( + + ) : ( + + )} @@ -108,5 +144,18 @@ const styles = StyleSheet.create({ marginVertical: 20, zIndex: 1, }, + recentsHeaderContainer: { + flexDirection: 'row', + }, + recentsHeader: { + fontSize: 17, + fontWeight: 'bold', + flexGrow: 1, + }, + clear: { + fontSize: 17, + fontWeight: 'bold', + color: '#698DD3', + }, }); export default SearchScreen; -- cgit v1.2.3-70-g09d2