aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/common/index.ts1
-rw-r--r--src/components/search/RecentSearches.tsx52
-rw-r--r--src/components/search/SearchResult.tsx53
-rw-r--r--src/routes/authentication/AuthProvider.tsx25
-rw-r--r--src/screens/search/SearchScreen.tsx53
5 files changed, 180 insertions, 4 deletions
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<ProfilePreviewType>;
+}
+/**
+ * An image component that returns the <Image> of the icon for a specific social media platform.
+ */
+const RecentSearches: React.FC<RecentSearchesProps> = (props) => {
+ return (
+ <>
+ <View style={styles.container}>
+ <Text style={styles.title}>{props.sectionTitle}</Text>
+ {props.sectionButtonTitle && (
+ <TouchableOpacity {...props}>
+ <Text style={styles.clear}>Clear all</Text>
+ </TouchableOpacity>
+ )}
+ </View>
+ <SearchResults results={props.recents} />
+ </>
+ );
+};
+
+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<SearchResultProps> = ({
};
}, [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 (
- <TouchableOpacity style={[styles.container, style]}>
+ <TouchableOpacity
+ onPress={addToRecentlyStored}
+ style={[styles.container, style]}>
<Image
style={styles.avatar}
source={
diff --git a/src/routes/authentication/AuthProvider.tsx b/src/routes/authentication/AuthProvider.tsx
index e52d56bc..589cb051 100644
--- a/src/routes/authentication/AuthProvider.tsx
+++ b/src/routes/authentication/AuthProvider.tsx
@@ -1,7 +1,13 @@
import React, {useEffect} from 'react';
import {createContext, useState} from 'react';
import RNFetchBlob from 'rn-fetch-blob';
-import {UserType, ProfileType, InstagramPostType} from '../../types';
+import AsyncStorage from '@react-native-community/async-storage';
+import {
+ UserType,
+ ProfileType,
+ InstagramPostType,
+ ProfilePreviewType,
+} from '../../types';
import {
PROFILE_INFO_ENDPOINT,
AVATAR_PHOTO_ENDPOINT,
@@ -17,6 +23,7 @@ interface AuthContextProps {
avatar: string | null;
cover: string | null;
instaPosts: Array<InstagramPostType>;
+ recentSearches: Array<ProfilePreviewType>;
}
const NO_USER: UserType = {
userId: '',
@@ -35,6 +42,7 @@ export const AuthContext = createContext<AuthContextProps>({
avatar: null,
cover: null,
instaPosts: [],
+ recentSearches: [],
});
/**
@@ -46,6 +54,9 @@ const AuthProvider: React.FC = ({children}) => {
const [avatar, setAvatar] = useState<string | null>(null);
const [cover, setCover] = useState<string | null>(null);
const [instaPosts, setInstaPosts] = useState<Array<InstagramPostType>>([]);
+ const [recentSearches, setRecentSearches] = useState<
+ Array<ProfilePreviewType>
+ >([]);
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}
</AuthContext.Provider>
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<number> = new Value(-SCREEN_HEIGHT);
const SearchScreen: React.FC = () => {
+ const {recentSearches} = React.useContext(AuthContext);
const [query, setQuery] = useState<string>('');
const [results, setResults] = useState<Array<ProfilePreviewType>>([]);
+ const [recents, setRecents] = useState<Array<ProfilePreviewType>>(
+ 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 (
<SearchBackground>
@@ -79,7 +106,7 @@ const SearchScreen: React.FC = () => {
<SearchBar
style={styles.searchBar}
onCancel={handleBlur}
- onChangeText={setQuery}
+ onChangeText={handleUpdate}
onBlur={Keyboard.dismiss}
onFocus={handleFocus}
value={query}
@@ -87,7 +114,16 @@ const SearchScreen: React.FC = () => {
/>
<Explore />
<SearchResultsBackground {...{top}}>
- <SearchResults {...{results}} />
+ {results.length === 0 && recents.length !== 0 ? (
+ <RecentSearches
+ sectionTitle="Recent"
+ sectionButtonTitle="Clear all"
+ onPress={clearRecentlySearched}
+ recents={recents}
+ />
+ ) : (
+ <SearchResults {...{results}} />
+ )}
</SearchResultsBackground>
</ScrollView>
<TabsGradient />
@@ -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;