diff options
-rw-r--r-- | src/assets/icons/badges/football.png | bin | 0 -> 5374 bytes | |||
-rw-r--r-- | src/components/profile/UniversityIcon.tsx | 16 | ||||
-rw-r--r-- | src/components/search/ExploreSection.tsx | 8 | ||||
-rw-r--r-- | src/components/search/ExploreSectionUser.tsx | 25 | ||||
-rw-r--r-- | src/constants/api.ts | 1 | ||||
-rw-r--r-- | src/routes/main/MainStackNavigator.tsx | 4 | ||||
-rw-r--r-- | src/routes/main/MainStackScreen.tsx | 8 | ||||
-rw-r--r-- | src/screens/suggestedPeople/MutualBadgeHolders.tsx | 207 | ||||
-rw-r--r-- | src/screens/suggestedPeople/SPBody.tsx | 31 | ||||
-rw-r--r-- | src/services/SuggestedPeopleService.ts | 24 |
10 files changed, 304 insertions, 20 deletions
diff --git a/src/assets/icons/badges/football.png b/src/assets/icons/badges/football.png Binary files differnew file mode 100644 index 00000000..2e8214b7 --- /dev/null +++ b/src/assets/icons/badges/football.png diff --git a/src/components/profile/UniversityIcon.tsx b/src/components/profile/UniversityIcon.tsx index 95aef8b9..48cfe3dc 100644 --- a/src/components/profile/UniversityIcon.tsx +++ b/src/components/profile/UniversityIcon.tsx @@ -1,11 +1,12 @@ import React from 'react'; -import {StyleSheet, ViewProps} from 'react-native'; +import {ImageStyle, StyleProp, StyleSheet, ViewProps} from 'react-native'; import {Image, Text, View} from 'react-native-animatable'; import {getUniversityClass, normalize} from '../../utils'; export interface UniversityIconProps extends ViewProps { university: string; - university_class: number; + university_class?: number; + imageStyle?: StyleProp<ImageStyle>; } /** @@ -15,6 +16,7 @@ const UniversityIcon: React.FC<UniversityIconProps> = ({ style, university, university_class, + imageStyle, }) => { var universityIcon; switch (university) { @@ -28,10 +30,12 @@ const UniversityIcon: React.FC<UniversityIconProps> = ({ return ( <View style={[styles.container, style]}> - <Image source={universityIcon} style={styles.icon} /> - <Text style={styles.univClass}> - {getUniversityClass(university_class)} - </Text> + <Image source={universityIcon} style={[styles.icon, imageStyle]} /> + {university_class && ( + <Text style={styles.univClass}> + {getUniversityClass(university_class)} + </Text> + )} </View> ); }; diff --git a/src/components/search/ExploreSection.tsx b/src/components/search/ExploreSection.tsx index e888370e..1af815db 100644 --- a/src/components/search/ExploreSection.tsx +++ b/src/components/search/ExploreSection.tsx @@ -21,7 +21,13 @@ const ExploreSection: React.FC<ExploreSectionProps> = ({title, users}) => { data={users} ListHeaderComponent={<View style={styles.padding} />} renderItem={({item: user}: {item: ProfilePreviewType}) => ( - <ExploreSectionUser key={user.id} user={user} style={styles.user} /> + <ExploreSectionUser + key={user.id} + user={user} + externalStyles={StyleSheet.create({ + container: styles.user, + })} + /> )} showsHorizontalScrollIndicator={false} horizontal diff --git a/src/components/search/ExploreSectionUser.tsx b/src/components/search/ExploreSectionUser.tsx index b0cfe5c6..6be8282b 100644 --- a/src/components/search/ExploreSectionUser.tsx +++ b/src/components/search/ExploreSectionUser.tsx @@ -1,7 +1,9 @@ import {useNavigation} from '@react-navigation/native'; import React, {useEffect, useState} from 'react'; +import {TextStyle, ViewStyle} from 'react-native'; import { Image, + StyleProp, StyleSheet, Text, TouchableOpacity, @@ -21,10 +23,11 @@ import {fetchUserX, normalize, userXInStore} from '../../utils'; interface ExploreSectionUserProps extends ViewProps { user: ProfilePreviewType; + externalStyles?: Record<string, StyleProp<ViewStyle | TextStyle>>; } const ExploreSectionUser: React.FC<ExploreSectionUserProps> = ({ user, - style, + externalStyles, }) => { const {id, username, first_name, last_name} = user; const [avatar, setAvatar] = useState<string | null>(null); @@ -59,7 +62,9 @@ const ExploreSectionUser: React.FC<ExploreSectionUserProps> = ({ }); }; return ( - <TouchableOpacity style={[styles.container, style]} onPress={handlePress}> + <TouchableOpacity + style={[styles.container, externalStyles?.container]} + onPress={handlePress}> <LinearGradient colors={['#9F00FF', '#27EAE9']} useAngle @@ -75,10 +80,12 @@ const ExploreSectionUser: React.FC<ExploreSectionUserProps> = ({ style={styles.profile} /> </LinearGradient> - <Text style={styles.name} numberOfLines={2}> + <Text style={[styles.name, externalStyles?.name]} numberOfLines={2}> {first_name} {last_name} </Text> - <Text style={styles.username} numberOfLines={1}>{`@${username}`}</Text> + <Text + style={[styles.username, externalStyles?.username]} + numberOfLines={1}>{`@${username}`}</Text> </TouchableOpacity> ); }; @@ -89,7 +96,7 @@ const styles = StyleSheet.create({ width: 100, }, gradient: { - height: 60, + height: 62, aspectRatio: 1, borderRadius: 40, justifyContent: 'center', @@ -104,13 +111,15 @@ const styles = StyleSheet.create({ name: { fontWeight: '600', flexWrap: 'wrap', - fontSize: normalize(16), + fontSize: normalize(14), + lineHeight: normalize(15), color: '#fff', textAlign: 'center', }, username: { - fontWeight: '400', - fontSize: normalize(14), + fontWeight: '500', + fontSize: normalize(11), + lineHeight: normalize(15), color: '#fff', }, }); diff --git a/src/constants/api.ts b/src/constants/api.ts index 0fc846c3..ab1ace74 100644 --- a/src/constants/api.ts +++ b/src/constants/api.ts @@ -37,6 +37,7 @@ export const ADD_USER_BADGES: string = API_URL + 'suggested_people/add_badges/'; // Suggested People export const SP_USERS_ENDPOINT: string = API_URL + 'suggested_people/'; export const SP_UPDATE_PICTURE_ENDPOINT: string = API_URL + 'suggested_people/update_picture/'; +export const SP_MUTUAL_BADGE_HOLDERS_ENDPOINT: string = SP_USERS_ENDPOINT + 'get_mutual_badge_holders/'; // Register as FCM device export const FCM_ENDPOINT: string = API_URL + 'fcm/'; diff --git a/src/routes/main/MainStackNavigator.tsx b/src/routes/main/MainStackNavigator.tsx index 901dd993..5cbe55d6 100644 --- a/src/routes/main/MainStackNavigator.tsx +++ b/src/routes/main/MainStackNavigator.tsx @@ -72,6 +72,10 @@ export type MainStackParams = { UpdateSPPicture: { goTo: string; }; + MutualBadgeHolders: { + badge_id: string; + badge_title: string; + }; }; export const MainStack = createStackNavigator<MainStackParams>(); diff --git a/src/routes/main/MainStackScreen.tsx b/src/routes/main/MainStackScreen.tsx index 91ed2f70..50802d06 100644 --- a/src/routes/main/MainStackScreen.tsx +++ b/src/routes/main/MainStackScreen.tsx @@ -1,9 +1,10 @@ import AsyncStorage from '@react-native-community/async-storage'; import {RouteProp} from '@react-navigation/native'; import {StackNavigationOptions} from '@react-navigation/stack'; -import React, {useEffect, useState} from 'react'; +import React, {useState} from 'react'; import {StyleSheet, Text} from 'react-native'; import {normalize} from 'react-native-elements'; +import MutualBadgeHolders from '../../screens/suggestedPeople/MutualBadgeHolders'; import BackIcon from '../../assets/icons/back-arrow.svg'; import { AnimatedTutorial, @@ -226,6 +227,11 @@ const MainStackScreen: React.FC<MainStackProps> = ({route}) => { ...headerBarOptions('white', ''), }} /> + <MainStack.Screen + name="MutualBadgeHolders" + component={MutualBadgeHolders} + options={{...modalStyle}} + /> </MainStack.Navigator> ); }; diff --git a/src/screens/suggestedPeople/MutualBadgeHolders.tsx b/src/screens/suggestedPeople/MutualBadgeHolders.tsx new file mode 100644 index 00000000..9742d72c --- /dev/null +++ b/src/screens/suggestedPeople/MutualBadgeHolders.tsx @@ -0,0 +1,207 @@ +import {RouteProp} from '@react-navigation/native'; +import {StackNavigationProp} from '@react-navigation/stack'; +import React, {useEffect, useState} from 'react'; +import {getMutualBadgeHolders} from '../../services'; +import {ProfilePreviewType} from '../../types'; +import {MainStackParams} from '../../routes/main/MainStackNavigator'; +import {Image, Text, View} from 'react-native-animatable'; +import {SafeAreaView} from 'react-native-safe-area-context'; +import {SCREEN_HEIGHT, SCREEN_WIDTH, isIPhoneX, normalize} from '../../utils'; +import LinearGradient from 'react-native-linear-gradient'; +import CloseIcon from '../../assets/ionicons/close-outline.svg'; +import {FlatList} from 'react-native-gesture-handler'; +import {StyleSheet, TouchableOpacity} from 'react-native'; +import ExploreSectionUser from '../../components/search/ExploreSectionUser'; + +type MutualBadgeHoldersRouteProps = RouteProp< + MainStackParams, + 'MutualBadgeHolders' +>; + +type MutualBadgeHoldersNavigationProps = StackNavigationProp< + MainStackParams, + 'MutualBadgeHolders' +>; + +interface MutualBadgeHoldersProps { + route: MutualBadgeHoldersRouteProps; + navigation: MutualBadgeHoldersNavigationProps; +} + +const MutualBadgeHolders: React.FC<MutualBadgeHoldersProps> = ({ + route, + navigation, +}) => { + const {badge_id, badge_title} = route.params; + const [users, setUsers] = useState<ProfilePreviewType[] | undefined>([]); + + useEffect(() => { + const getUsers = async (badge_id: string) => { + let localUsers: + | ProfilePreviewType[] + | undefined = await getMutualBadgeHolders(badge_id); + setUsers(localUsers); + }; + getUsers(badge_id); + }, [badge_id]); + + return ( + <SafeAreaView> + <View style={styles.mainContainer}> + <View style={styles.mainContentContainer}> + <TouchableOpacity + style={styles.closeButton} + onPress={() => { + navigation.goBack(); + }}> + <CloseIcon height={'100%'} width={'100%'} color={'grey'} /> + </TouchableOpacity> + <View style={styles.iconView}> + <LinearGradient + colors={['#4E3629', '#EC2027']} + useAngle={true} + angle={154.72} + angleCenter={{x: 0.5, y: 0.5}} + style={styles.badgeBackground}> + {/* TODO: Insert image link according to badge_id passed. + * Awaiting final images from product + */} + <Image + source={require('../../assets/icons/badges/football.png')} + style={{width: SCREEN_WIDTH * 0.1, height: SCREEN_WIDTH * 0.1}} + /> + </LinearGradient> + </View> + <View style={styles.headerContainer}> + <Text style={styles.heading}>{badge_title}</Text> + <Text style={styles.subHeading}>See who else has this badge!</Text> + </View> + <FlatList + data={users} + numColumns={3} + columnWrapperStyle={styles.columnWrapperStyle} + renderItem={(user) => { + return ( + <ExploreSectionUser + key={user.item.id} + user={user.item} + externalStyles={exploreUserStyle} + /> + ); + }} + keyExtractor={(item, index) => index.toString()} + showsVerticalScrollIndicator={false} + style={styles.flatlistContainer} + contentContainerStyle={styles.flatlistContentContainer} + /> + <View /> + </View> + </View> + </SafeAreaView> + ); +}; + +const styles = StyleSheet.create({ + mainContainer: { + flexDirection: 'column', + justifyContent: 'flex-end', + width: SCREEN_WIDTH, + height: isIPhoneX() ? SCREEN_HEIGHT * 0.85 : SCREEN_HEIGHT * 0.88, + }, + mainContentContainer: { + backgroundColor: '#fff', + width: SCREEN_WIDTH * 0.93, + height: SCREEN_HEIGHT * 0.64, + alignSelf: 'center', + bottom: '2.5%', + borderColor: '#fff', + borderWidth: 1, + borderRadius: 5, + }, + closeButton: { + position: 'absolute', + height: normalize(20), + width: normalize(20), + left: '3%', + top: '2%', + }, + badgeBackground: { + position: 'absolute', + width: '100%', + height: '100%', + borderRadius: 50, + borderColor: 'transparent', + borderWidth: 1, + alignSelf: 'center', + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center', + }, + iconView: { + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center', + width: SCREEN_WIDTH * 0.2, + height: SCREEN_WIDTH * 0.2, + alignSelf: 'center', + top: -SCREEN_WIDTH * 0.1, + }, + headerContainer: { + top: '-5%', + width: '100%', + height: '9%', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'space-evenly', + }, + heading: { + fontSize: normalize(17), + fontWeight: '600', + lineHeight: normalize(20), + color: '#000', + }, + subHeading: { + fontSize: normalize(11), + fontWeight: '600', + lineHeight: normalize(15), + color: '#828282', + }, + columnWrapperStyle: { + width: SCREEN_WIDTH * 0.85, + flexDirection: 'row', + flexGrow: 0, + alignSelf: 'center', + justifyContent: 'space-between', + marginBottom: '8%', + }, + flatlistContainer: { + width: SCREEN_WIDTH * 0.85, + alignSelf: 'center', + flexDirection: 'column', + top: '-1%', + }, + flatlistContentContainer: { + width: SCREEN_WIDTH * 0.85, + paddingBottom: 20, + alignSelf: 'center', + }, +}); + +const exploreUserStyle = StyleSheet.create({ + container: {}, + name: { + fontWeight: '600', + flexWrap: 'wrap', + fontSize: normalize(12), + lineHeight: normalize(15), + color: '#000', + textAlign: 'center', + }, + username: { + fontWeight: '500', + fontSize: normalize(10), + lineHeight: normalize(15), + color: '#000', + }, +}); +export default MutualBadgeHolders; diff --git a/src/screens/suggestedPeople/SPBody.tsx b/src/screens/suggestedPeople/SPBody.tsx index aa97dc94..21e86f14 100644 --- a/src/screens/suggestedPeople/SPBody.tsx +++ b/src/screens/suggestedPeople/SPBody.tsx @@ -12,8 +12,8 @@ import { ScreenType, SuggestedPeopleDataType, } from '../../types'; -import {normalize, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; - +import {isIPhoneX, normalize, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; +import UniversityIcon from '../../components/profile/UniversityIcon'; interface SPBodyProps { item: SuggestedPeopleDataType; index: number; @@ -87,7 +87,22 @@ const SPBody: React.FC<SPBodyProps> = ({ <View> {backgroundImage} <View style={styles.mainContainer}> - <Text style={styles.title}>{firstItem && 'Suggested People'}</Text> + <View style={styles.topContainer}> + <Text style={styles.title}>{firstItem && 'Suggested People'}</Text> + <TouchableOpacity + onPress={() => + navigation.navigate('MutualBadgeHolders', { + badge_id: 40, + badge_title: 'Brown University Football', + }) + }> + <UniversityIcon + university="brown" + style={styles.universityIconContainer} + imageStyle={{width: normalize(31), height: normalize(38)}} + /> + </TouchableOpacity> + </View> <View style={styles.body}> <View style={styles.marginManager}> <View style={styles.addUserContainer}> @@ -132,6 +147,16 @@ const styles = StyleSheet.create({ justifyContent: 'space-between', alignSelf: 'center', }, + topContainer: { + height: isIPhoneX() ? SCREEN_HEIGHT * 0.11 : SCREEN_HEIGHT * 0.13, + flexDirection: 'column', + justifyContent: 'space-between', + }, + universityIconContainer: { + width: normalize(31), + height: normalize(38), + left: '5%', + }, marginManager: {marginHorizontal: '5%'}, image: { position: 'absolute', diff --git a/src/services/SuggestedPeopleService.ts b/src/services/SuggestedPeopleService.ts index aa1fabde..c57de59d 100644 --- a/src/services/SuggestedPeopleService.ts +++ b/src/services/SuggestedPeopleService.ts @@ -1,10 +1,11 @@ import AsyncStorage from '@react-native-community/async-storage'; import { EDIT_PROFILE_ENDPOINT, + SP_MUTUAL_BADGE_HOLDERS_ENDPOINT, SP_UPDATE_PICTURE_ENDPOINT, SP_USERS_ENDPOINT, } from '../constants'; -import {SuggestedPeopleDataType} from '../types'; +import {ProfilePreviewType, SuggestedPeopleDataType} from '../types'; export const sendSuggestedPeopleLinked = async ( userId: string, @@ -97,3 +98,24 @@ export const getSuggestedPeopleProfile = async (userId: string) => { return undefined; } }; + +export const getMutualBadgeHolders = async (badgeId: string) => { + try { + const token = await AsyncStorage.getItem('token'); + const response = await fetch(SP_MUTUAL_BADGE_HOLDERS_ENDPOINT, { + method: 'GET', + headers: { + Authorization: 'Token ' + token, + }, + }); + if (response.status === 200) { + const data: ProfilePreviewType[] = await response.json(); + return data; + } else { + return undefined; + } + } catch (error) { + console.log('Error retrieving SP info'); + return undefined; + } +}; |