aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIvan Chen <ivan@tagg.id>2021-03-08 16:27:08 -0500
committerGitHub <noreply@github.com>2021-03-08 16:27:08 -0500
commitd77f43663fbe409b011b5509bcbff3d07f8ded55 (patch)
tree6bef0a405a92185d08b0850d199ee507e8bfe90c /src
parent7e5f9c63360f8c4505bb414384e13f8c0f7576e4 (diff)
parent973c8a03681724f2e303fcd021301764bfc4717c (diff)
Merge pull request #286 from leonyjiang/tma660-recently-searched-categories
[TMA-660] Recently Searched Categories
Diffstat (limited to 'src')
-rw-r--r--src/components/search/RecentSearches.tsx51
-rw-r--r--src/components/search/SearchResultCell.tsx33
-rw-r--r--src/components/search/SearchResultList.tsx1
-rw-r--r--src/components/search/SearchResults.tsx52
-rw-r--r--src/components/search/SearchResultsBackground.tsx13
-rw-r--r--src/screens/search/SearchScreen.tsx55
-rw-r--r--src/services/ExploreService.ts1
-rw-r--r--src/services/SocialLinkingService.ts1
-rw-r--r--src/services/SuggestedPeopleService.ts2
-rw-r--r--src/services/UserFriendsService.ts1
-rw-r--r--src/types/types.ts5
-rw-r--r--src/utils/users.ts36
12 files changed, 177 insertions, 74 deletions
diff --git a/src/components/search/RecentSearches.tsx b/src/components/search/RecentSearches.tsx
index 6fb9fca9..b4cc5483 100644
--- a/src/components/search/RecentSearches.tsx
+++ b/src/components/search/RecentSearches.tsx
@@ -7,37 +7,45 @@ import {
TouchableOpacityProps,
ScrollView,
} from 'react-native';
-import {PreviewType, ProfilePreviewType, ScreenType} from '../../types';
+import {
+ PreviewType,
+ ProfilePreviewType,
+ ScreenType,
+ CategoryPreviewType,
+} from '../../types';
import {TAGG_LIGHT_BLUE} from '../../constants';
import SearchResults from './SearchResults';
import {SCREEN_HEIGHT} from '../../utils';
interface RecentSearchesProps extends TouchableOpacityProps {
sectionTitle: PreviewType;
- sectionButtonTitle: string;
recents: Array<ProfilePreviewType>;
+ recentCategories: CategoryPreviewType[];
screenType: ScreenType;
}
-/**
- * An image component that returns the <Image> of the icon for a specific social media platform.
- */
+
const RecentSearches: React.FC<RecentSearchesProps> = (props) => {
+ const {
+ sectionTitle,
+ recents,
+ recentCategories,
+ screenType,
+ } = props;
return (
<ScrollView
style={styles.mainContainer}
- contentContainerStyle={{paddingBottom: SCREEN_HEIGHT * 0.1}}>
- <View style={styles.container}>
- <Text style={styles.title}>{props.sectionTitle}</Text>
- {props.sectionButtonTitle && (
- <TouchableOpacity {...props}>
- <Text style={styles.clear}>Clear all</Text>
- </TouchableOpacity>
- )}
+ contentContainerStyle={styles.contentContainer}>
+ <View style={styles.header}>
+ <Text style={styles.title}>{sectionTitle}</Text>
+ <TouchableOpacity {...props}>
+ <Text style={styles.clear}>Clear all</Text>
+ </TouchableOpacity>
</View>
<SearchResults
- results={props.recents}
- previewType={props.sectionTitle}
- screenType={props.screenType}
+ results={recents}
+ categories={recentCategories}
+ previewType={sectionTitle}
+ screenType={screenType}
/>
</ScrollView>
);
@@ -45,17 +53,20 @@ const RecentSearches: React.FC<RecentSearchesProps> = (props) => {
const styles = StyleSheet.create({
mainContainer: {
- marginLeft: '3%',
- padding: 20,
+ flexGrow: 1,
+ },
+ contentContainer: {
+ paddingBottom: SCREEN_HEIGHT * 0.1,
},
- container: {
+ header: {
+ paddingHorizontal: 25,
+ paddingVertical: 5,
flexDirection: 'row',
},
title: {
fontSize: 18,
fontWeight: '600',
flexGrow: 1,
- marginBottom: '5%',
},
clear: {
fontSize: 18,
diff --git a/src/components/search/SearchResultCell.tsx b/src/components/search/SearchResultCell.tsx
index 0f6f5b7d..e0351d96 100644
--- a/src/components/search/SearchResultCell.tsx
+++ b/src/components/search/SearchResultCell.tsx
@@ -6,7 +6,12 @@ import {useDispatch, useStore} from 'react-redux';
import {ERROR_UNABLE_TO_VIEW_PROFILE} from '../../constants/strings';
import {loadImageFromURL} from '../../services';
import {RootState} from '../../store/rootReducer';
-import {ProfilePreviewType, ScreenType, UserType} from '../../types';
+import {
+ ProfilePreviewType,
+ ScreenType,
+ UserType,
+ CategoryPreviewType,
+} from '../../types';
import {normalize, SCREEN_WIDTH} from '../../utils';
import {
addUserToRecentlyViewed,
@@ -14,6 +19,7 @@ import {
defaultUserProfile,
fetchUserX,
userXInStore,
+ addCategoryToRecentlySearched,
} from '../../utils/users';
interface SearchResults {
@@ -66,7 +72,7 @@ const SearchResultsCell: React.FC<SearchResults> = ({
return;
}
- await addUserToRecentlyViewed({
+ addUserToRecentlyViewed({
id,
first_name,
last_name,
@@ -99,6 +105,17 @@ const SearchResultsCell: React.FC<SearchResults> = ({
}
};
+ /*
+ * Save selected category in recently-searched categories and navigate to its Discover screen.
+ */
+ const onPressCategory = async () => {
+ const categoryObj: CategoryPreviewType = {name, category};
+ addCategoryToRecentlySearched(categoryObj);
+ navigation.navigate('DiscoverUsers', {
+ searchCategory: {id, name},
+ });
+ };
+
const userCell = () => {
return (
<TouchableOpacity
@@ -129,13 +146,7 @@ const SearchResultsCell: React.FC<SearchResults> = ({
const categoryCell = () => {
return (
- <TouchableOpacity
- style={styles.cellContainer}
- onPress={() =>
- navigation.navigate('DiscoverUsers', {
- searchCategory: {id, name},
- })
- }>
+ <TouchableOpacity style={styles.cellContainer} onPress={onPressCategory}>
<View style={[styles.imageContainer, styles.categoryBackground]}>
<Image
resizeMode="contain"
@@ -156,8 +167,8 @@ const SearchResultsCell: React.FC<SearchResults> = ({
const styles = StyleSheet.create({
cellContainer: {
flexDirection: 'row',
- marginHorizontal: SCREEN_WIDTH * 0.08,
- marginBottom: SCREEN_WIDTH * 0.08,
+ paddingHorizontal: 25,
+ paddingVertical: 15,
},
imageContainer: {
width: SCREEN_WIDTH * 0.112,
diff --git a/src/components/search/SearchResultList.tsx b/src/components/search/SearchResultList.tsx
index 14d5de6d..41c8c8b2 100644
--- a/src/components/search/SearchResultList.tsx
+++ b/src/components/search/SearchResultList.tsx
@@ -70,7 +70,6 @@ const SearchResultList: React.FC<SearchResultsProps> = ({
const styles = StyleSheet.create({
container: {
- marginTop: SCREEN_HEIGHT * 0.02,
height: SCREEN_HEIGHT,
},
sectionHeaderStyle: {
diff --git a/src/components/search/SearchResults.tsx b/src/components/search/SearchResults.tsx
index bf355220..798d3251 100644
--- a/src/components/search/SearchResults.tsx
+++ b/src/components/search/SearchResults.tsx
@@ -1,22 +1,33 @@
import React from 'react';
-import {ProfilePreviewType, PreviewType, ScreenType} from '../../types';
+import {
+ ProfilePreviewType,
+ PreviewType,
+ ScreenType,
+ CategoryPreviewType,
+} from '../../types';
import ProfilePreview from '../profile/ProfilePreview';
import {StyleSheet, View} from 'react-native';
+import SearchResultsCell from './SearchResultCell';
+import {useSelector} from 'react-redux';
+import {RootState} from 'src/store/rootReducer';
interface SearchResultsProps {
- results: Array<ProfilePreviewType>;
+ results: ProfilePreviewType[];
previewType: PreviewType;
screenType: ScreenType;
+ categories: CategoryPreviewType[];
}
const SearchResults: React.FC<SearchResultsProps> = ({
results,
previewType,
screenType,
+ categories,
}) => {
/**
* Added the following swicth case to make Results on Search and Recents screen a list
* Flex is love
*/
- var containerStyle;
+ const {user: loggedInUser} = useSelector((state: RootState) => state.user);
+ let containerStyle;
switch (previewType) {
case 'Search':
containerStyle = styles.containerSearch;
@@ -29,25 +40,32 @@ const SearchResults: React.FC<SearchResultsProps> = ({
}
return (
<View style={containerStyle}>
- {results &&
- results.map((profilePreview) => (
- <ProfilePreview
- style={styles.result}
- key={profilePreview.id}
- {...{profilePreview}}
- previewType={previewType}
- screenType={screenType}
- />
- ))}
+ {categories.map((category: CategoryPreviewType) => (
+ <SearchResultsCell
+ key={category.name}
+ profileData={category}
+ {...{loggedInUser}}
+ />
+ ))}
+ {results.map((profile: ProfilePreviewType) => (
+ <SearchResultsCell
+ key={profile.id}
+ profileData={profile}
+ {...{loggedInUser}}
+ />
+ ))}
</View>
);
};
const styles = StyleSheet.create({
- containerSearch: {flexDirection: 'column', flexWrap: 'wrap'},
- container: {flexDirection: 'row', flexWrap: 'wrap'},
- result: {
- marginVertical: 10,
+ containerSearch: {
+ flexDirection: 'column',
+ flexWrap: 'wrap',
+ },
+ container: {
+ flexDirection: 'row',
+ flexWrap: 'wrap',
},
});
diff --git a/src/components/search/SearchResultsBackground.tsx b/src/components/search/SearchResultsBackground.tsx
index f6f40f52..25dcf781 100644
--- a/src/components/search/SearchResultsBackground.tsx
+++ b/src/components/search/SearchResultsBackground.tsx
@@ -21,7 +21,7 @@ const SearchResultsBackground: React.FC<SearchResultsBackgroundProps> = ({
return (
<Animated.View
style={[styles.container, {opacity: opacityBackground, top}]}>
- <Animated.View style={[styles.results, {opacity: opacityContent}]}>
+ <Animated.View style={{opacity: opacityContent}}>
{children}
</Animated.View>
</Animated.View>
@@ -33,15 +33,10 @@ const styles = StyleSheet.create({
height: SCREEN_HEIGHT,
width: SCREEN_WIDTH,
position: 'absolute',
- backgroundColor: '#fff',
+ backgroundColor: 'white',
+ paddingTop: 60,
+ paddingBottom: 10,
zIndex: 0,
},
- contentContainer: {
- flexGrow: 1,
- paddingBottom: SCREEN_HEIGHT / 15,
- },
- results: {
- marginTop: StatusBarHeight,
- },
});
export default SearchResultsBackground;
diff --git a/src/screens/search/SearchScreen.tsx b/src/screens/search/SearchScreen.tsx
index 223fc2b2..835a5622 100644
--- a/src/screens/search/SearchScreen.tsx
+++ b/src/screens/search/SearchScreen.tsx
@@ -17,7 +17,7 @@ import {SEARCH_ENDPOINT, TAGG_LIGHT_BLUE} from '../../constants';
import {loadSearchResults} from '../../services';
import {resetScreenType} from '../../store/actions';
import {RootState} from '../../store/rootReducer';
-import {ProfilePreviewType, ScreenType} from '../../types';
+import {ProfilePreviewType, ScreenType, CategoryPreviewType} from '../../types';
import {normalize, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
/**
@@ -32,6 +32,9 @@ const SearchScreen: React.FC = () => {
const [recents, setRecents] = useState<Array<ProfilePreviewType>>(
recentSearches ?? [],
);
+ const [recentCategories, setRecentCategories] = useState<
+ CategoryPreviewType[]
+ >([]);
const [searching, setSearching] = useState(false);
const top = Animated.useValue(-SCREEN_HEIGHT);
const [keyboardVisible, setKeyboardVisible] = React.useState(
@@ -50,7 +53,18 @@ const SearchScreen: React.FC = () => {
}, []);
const dispatch = useDispatch();
+ /*
+ * If user begins actively searching, refresh recently-searched list.
+ */
+ useEffect(() => {
+ if (searching) loadRecentSearches();
+ }, [searching]);
+
+ /*
+ * Main handler for changes in query.
+ */
useEffect(() => {
+ if (!query.length) loadRecentSearches();
if (query.length < 3) {
setResults(undefined);
return;
@@ -97,17 +111,17 @@ const SearchScreen: React.FC = () => {
timing(top, topInConfig).start();
setSearching(true);
};
- const handleBlur = () => {
- setQuery('');
+ const handleCancel = () => {
Keyboard.dismiss();
const topOutConfig = {
duration: 180,
toValue: -SCREEN_HEIGHT,
easing: Easing.inOut(Easing.ease),
};
- timing(top, topOutConfig).start();
+ timing(top, topOutConfig).start(() => setQuery(''));
setSearching(false);
};
+
const loadRecentlySearchedUsers = async () => {
try {
const asyncCache = await AsyncStorage.getItem('@recently_searched_users');
@@ -116,17 +130,31 @@ const SearchScreen: React.FC = () => {
console.log(e);
}
};
- const clearRecentlySearched = async () => {
+ const loadRecentlySearchedCategories = async () => {
try {
- await AsyncStorage.removeItem('@recently_searched_users');
- loadRecentlySearchedUsers();
+ const recentCategoriesJSON = await AsyncStorage.getItem(
+ '@recently_searched_categories',
+ );
+ setRecentCategories(
+ recentCategoriesJSON ? JSON.parse(recentCategoriesJSON) : [],
+ );
} catch (e) {
console.log(e);
}
};
- const handleUpdate = async (val: string) => {
- setQuery(val);
+ const loadRecentSearches = () => {
loadRecentlySearchedUsers();
+ loadRecentlySearchedCategories();
+ };
+ const clearRecentlySearched = async () => {
+ try {
+ AsyncStorage.removeItem('@recently_searched_users');
+ AsyncStorage.removeItem('@recently_searched_categories');
+ loadRecentlySearchedUsers();
+ loadRecentlySearchedCategories();
+ } catch (e) {
+ console.log(e);
+ }
};
return (
@@ -141,8 +169,8 @@ const SearchScreen: React.FC = () => {
showsVerticalScrollIndicator={false}>
<SearchBar
style={styles.searchBar}
- onCancel={handleBlur}
- onChangeText={handleUpdate}
+ onCancel={handleCancel}
+ onChangeText={setQuery}
onBlur={Keyboard.dismiss}
onFocus={handleFocus}
value={query}
@@ -150,13 +178,14 @@ const SearchScreen: React.FC = () => {
/>
<SearchCategories />
<SearchResultsBackground {...{top}}>
- {results === undefined && recents.length !== 0 ? (
+ {results === undefined &&
+ recents.length + recentCategories.length !== 0 ? (
<RecentSearches
sectionTitle="Recent"
sectionButtonTitle="Clear all"
onPress={clearRecentlySearched}
- recents={recents}
screenType={ScreenType.Search}
+ {...{recents, recentCategories}}
/>
) : (
<SearchResultList
diff --git a/src/services/ExploreService.ts b/src/services/ExploreService.ts
index 33b79b4a..56a2e3d1 100644
--- a/src/services/ExploreService.ts
+++ b/src/services/ExploreService.ts
@@ -51,6 +51,7 @@ export const getAllExploreSections = async () => {
return EMPTY_PROFILE_PREVIEW_LIST;
}
const data = await response.json();
+ // TODO (if we return to original explore format): get keys from backend API
const exploreSections: Record<ExploreSectionType, ProfilePreviewType[]> = {
'New to Tagg': data.categories.new_to_tagg,
'People You May Know': data.categories.people_you_may_know,
diff --git a/src/services/SocialLinkingService.ts b/src/services/SocialLinkingService.ts
index d1c5c2ff..90b26c96 100644
--- a/src/services/SocialLinkingService.ts
+++ b/src/services/SocialLinkingService.ts
@@ -13,7 +13,6 @@ import {
LINK_TWITTER_OAUTH,
} from '../constants';
import {COMING_SOON_MSG, ERROR_LINK, SUCCESS_LINK} from '../constants/strings';
-import {CategorySelection} from '../screens';
// A list of endpoint strings for all the integrated socials
export const integratedEndpoints: {[social: string]: [string, string]} = {
diff --git a/src/services/SuggestedPeopleService.ts b/src/services/SuggestedPeopleService.ts
index c57de59d..d0032458 100644
--- a/src/services/SuggestedPeopleService.ts
+++ b/src/services/SuggestedPeopleService.ts
@@ -99,7 +99,7 @@ export const getSuggestedPeopleProfile = async (userId: string) => {
}
};
-export const getMutualBadgeHolders = async (badgeId: string) => {
+export const getMutualBadgeHolders = async () => {
try {
const token = await AsyncStorage.getItem('token');
const response = await fetch(SP_MUTUAL_BADGE_HOLDERS_ENDPOINT, {
diff --git a/src/services/UserFriendsService.ts b/src/services/UserFriendsService.ts
index a0bf7ac7..dbec1974 100644
--- a/src/services/UserFriendsService.ts
+++ b/src/services/UserFriendsService.ts
@@ -25,7 +25,6 @@ export const loadFriends = async (userId: string, token: string) => {
};
export const friendOrUnfriendUser = async (
- user: string,
friend: string,
token: string,
friendship_status: FriendshipStatusType,
diff --git a/src/types/types.ts b/src/types/types.ts
index 186cb4d5..7839e3f5 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -14,6 +14,11 @@ export interface ProfilePreviewType {
thumbnail_url: string;
}
+export interface CategoryPreviewType {
+ name: string;
+ category: string;
+}
+
export type FriendshipStatusType = 'friends' | 'requested' | 'no_record';
export interface ProfileType {
diff --git a/src/utils/users.ts b/src/utils/users.ts
index 653c941e..15107c99 100644
--- a/src/utils/users.ts
+++ b/src/utils/users.ts
@@ -19,6 +19,7 @@ import {AppDispatch} from './../store/configureStore';
import {RootState} from './../store/rootReducer';
import {
ProfilePreviewType,
+ CategoryPreviewType,
ProfileType,
ScreenType,
UserType,
@@ -196,3 +197,38 @@ export const addUserToRecentlyViewed = async (user: ProfilePreviewType) => {
console.log(e);
}
};
+
+/*
+ * Stores `category` in AsyncStorage as a recently searched category.
+ */
+export const addCategoryToRecentlySearched = async (
+ category: CategoryPreviewType,
+) => {
+ const recentlySearchedCategoriesKey = '@recently_searched_categories';
+ let categories: CategoryPreviewType[];
+ // retrieve recently-searched categories and set new list
+ try {
+ const categoriesJSON = await AsyncStorage.getItem(
+ recentlySearchedCategoriesKey,
+ );
+ if (categoriesJSON) {
+ categories = JSON.parse(categoriesJSON);
+ // TODO: make this more efficient by comparing shorter key
+ if (categories.find((c) => c.name === category.name)) return;
+ categories.push(category);
+ } else {
+ categories = [category];
+ }
+ // store updated list of recently-searched categories
+ try {
+ AsyncStorage.setItem(
+ recentlySearchedCategoriesKey,
+ JSON.stringify(categories),
+ );
+ } catch (e) {
+ console.log(e);
+ }
+ } catch (e) {
+ console.log(e);
+ }
+};