aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIvan Chen <ivan@tagg.id>2021-03-09 17:03:03 -0500
committerGitHub <noreply@github.com>2021-03-09 17:03:03 -0500
commitd45f9fa30bca546989bcb7a97da2999a80c4e56a (patch)
tree373d6d1db1c11a53ee0205e689436dafce9048e2 /src
parent1033701a5f6a200a6bc09b99e77e912e3df2103f (diff)
parentd66c0fa6d0b75918e4c67a61754c4e2f16bfaf3e (diff)
Merge pull request #288 from shravyaramesh/tma689-badge-animation
[TMA-689] Badge animation
Diffstat (limited to 'src')
-rw-r--r--src/assets/universities/brown-clicked.pngbin0 -> 10943 bytes
-rw-r--r--src/components/search/SearchCategories.tsx2
-rw-r--r--src/components/search/SearchResultList.tsx2
-rw-r--r--src/components/search/SearchResults.tsx4
-rw-r--r--src/components/suggestedPeople/BadgesDropdown.tsx169
-rw-r--r--src/components/suggestedPeople/UniversityIconClicked.tsx61
-rw-r--r--src/components/suggestedPeople/index.ts1
-rw-r--r--src/screens/suggestedPeople/SPBody.tsx151
-rw-r--r--src/screens/suggestedPeople/SuggestedPeopleScreen.tsx28
-rw-r--r--src/types/types.ts2
10 files changed, 274 insertions, 146 deletions
diff --git a/src/assets/universities/brown-clicked.png b/src/assets/universities/brown-clicked.png
new file mode 100644
index 00000000..8f72f244
--- /dev/null
+++ b/src/assets/universities/brown-clicked.png
Binary files differ
diff --git a/src/components/search/SearchCategories.tsx b/src/components/search/SearchCategories.tsx
index 2883a541..c3c4c518 100644
--- a/src/components/search/SearchCategories.tsx
+++ b/src/components/search/SearchCategories.tsx
@@ -4,7 +4,7 @@ import {StyleSheet, Text, View} from 'react-native';
import {TouchableOpacity} from 'react-native-gesture-handler';
import LinearGradient from 'react-native-linear-gradient';
import {getButtons} from '../../services/ExploreService';
-import {SearchCategoryType} from 'src/types';
+import {SearchCategoryType} from '../../types';
import {TAGG_LIGHT_BLUE_2, TAGG_PURPLE} from '../../constants';
import {SCREEN_WIDTH} from '../../utils';
diff --git a/src/components/search/SearchResultList.tsx b/src/components/search/SearchResultList.tsx
index 613ab734..d8cf02d9 100644
--- a/src/components/search/SearchResultList.tsx
+++ b/src/components/search/SearchResultList.tsx
@@ -1,7 +1,7 @@
import React, {useEffect, useState} from 'react';
import {SectionList, StyleSheet, Text, View} from 'react-native';
import {useSelector} from 'react-redux';
-import {RootState} from 'src/store/rootreducer';
+import {RootState} from '../../store/rootreducer';
import {NO_RESULTS_FOUND} from '../../constants/strings';
import {PreviewType, ScreenType} from '../../types';
import {normalize, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
diff --git a/src/components/search/SearchResults.tsx b/src/components/search/SearchResults.tsx
index fbeae1d8..277b3454 100644
--- a/src/components/search/SearchResults.tsx
+++ b/src/components/search/SearchResults.tsx
@@ -5,10 +5,10 @@ import {
ScreenType,
CategoryPreviewType,
} from '../../types';
-import {StyleSheet, View} from 'react-native';
+import {View} from 'react-native';
import SearchResultsCell from './SearchResultCell';
import {useSelector} from 'react-redux';
-import {RootState} from 'src/store/rootReducer';
+import {RootState} from '../../store/rootReducer';
interface SearchResultsProps {
results: ProfilePreviewType[];
previewType: PreviewType;
diff --git a/src/components/suggestedPeople/BadgesDropdown.tsx b/src/components/suggestedPeople/BadgesDropdown.tsx
new file mode 100644
index 00000000..70c70e47
--- /dev/null
+++ b/src/components/suggestedPeople/BadgesDropdown.tsx
@@ -0,0 +1,169 @@
+import {useNavigation} from '@react-navigation/native';
+import React, {useEffect, useState} from 'react';
+import {Image, StyleSheet} from 'react-native';
+import {TouchableOpacity} from 'react-native-gesture-handler';
+import LinearGradient from 'react-native-linear-gradient';
+import Animated, {Easing} from 'react-native-reanimated';
+import {UniversityBadge} from 'src/types';
+import {UniversityIcon} from '..';
+import {normalize, SCREEN_WIDTH} from '../../utils';
+import UniversityIconClicked from './UniversityIconClicked';
+interface BadgesDropdownProps {
+ localBadges: {
+ badge: UniversityBadge;
+ img: string;
+ }[];
+ badges: UniversityBadge[];
+}
+
+const BadgesDropdown: React.FC<BadgesDropdownProps> = ({
+ localBadges,
+ badges,
+}) => {
+ // Used to toggle between dropdown being displayed and not
+ const [displayBadges, setDisplayBadges] = useState<boolean>(false);
+
+ // Determines the absolute position of the individual badges [0, i * 40]
+ let [top, setTop] = useState<Animated.Value<number>[]>([]);
+ const navigation = useNavigation();
+
+ useEffect(() => {
+ // Initialize position of badges to 0
+ const defineBadgePositions = () => {
+ let localTop: Animated.Value<number>[] = [];
+ badges.forEach(() => {
+ localTop.push(new Animated.Value(0));
+ });
+ setTop(localTop);
+ };
+ defineBadgePositions();
+ }, []);
+
+ // Displays badges dropdown by updating top [state] for every badge
+ const animate = () => {
+ for (let i = 0; i < badges?.length; i++) {
+ if (top) {
+ Animated.timing(top[i], {
+ toValue: i * 40 + 50,
+ duration: 150,
+ easing: Easing.linear,
+ }).start();
+ }
+ }
+ };
+
+ // Draws back displayed badges by setting top [state] to 0 for every badge
+ const animateBack = () => {
+ for (let i = 0; i < badges?.length; i++) {
+ if (top) {
+ Animated.timing(top[i], {
+ toValue: 0,
+ duration: 150,
+ easing: Easing.linear,
+ }).start();
+ }
+ }
+ };
+
+ return (
+ <Animated.View
+ style={[styles.badgesContainer, {height: 50 + 40 * localBadges.length}]}>
+ <TouchableOpacity
+ activeOpacity={1}
+ onPress={() => {
+ const updatedBadges = !displayBadges
+ setDisplayBadges(updatedBadges);
+ if (updatedBadges) {
+ animate();
+ } else {
+ animateBack();
+ }
+ }}>
+ {displayBadges ? (
+ <UniversityIconClicked
+ university="brown"
+ style={styles.universityIconContainer}
+ imageStyle={{width: normalize(31), height: normalize(38)}}
+ />
+ ) : (
+ <UniversityIcon
+ university="brown"
+ style={styles.universityIconContainer}
+ imageStyle={{width: normalize(31), height: normalize(38)}}
+ />
+ )}
+ </TouchableOpacity>
+ {localBadges &&
+ localBadges.map(({badge, img}, index) => (
+ <Animated.View
+ key={badge.id}
+ style={[
+ styles.animatedBadgeView,
+ {
+ top: top[index],
+ zIndex: -1 * badge.id,
+ },
+ ]}>
+ <TouchableOpacity
+ style={styles.badgeButton}
+ onPress={() => {
+ navigation.navigate('MutualBadgeHolders', {
+ badge_id: badge.id,
+ badge_title: badge.name,
+ });
+ }}>
+ <LinearGradient
+ colors={['#4E3629', '#EC2027']}
+ useAngle={true}
+ angle={154.72}
+ angleCenter={{x: 0.5, y: 0.5}}
+ style={styles.badgeBackground}>
+ <Image
+ source={img}
+ style={{
+ width: SCREEN_WIDTH * 0.04,
+ height: SCREEN_WIDTH * 0.04,
+ }}
+ />
+ </LinearGradient>
+ </TouchableOpacity>
+ </Animated.View>
+ ))}
+ </Animated.View>
+ );
+};
+
+const styles = StyleSheet.create({
+ badgeBackground: {
+ position: 'absolute',
+ width: '100%',
+ height: '100%',
+ borderRadius: 50,
+ borderColor: 'transparent',
+ borderWidth: 1,
+ alignSelf: 'center',
+ flexDirection: 'row',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ badgesContainer: {
+ flexDirection: 'column',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ width: 38,
+ left: '5%',
+ paddingBottom: '2%',
+ },
+ badgeButton: {
+ width: 30,
+ height: 30,
+ borderRadius: 15,
+ },
+ animatedBadgeView: {position: 'absolute'},
+ universityIconContainer: {
+ width: normalize(31),
+ height: normalize(38),
+ },
+});
+
+export default BadgesDropdown;
diff --git a/src/components/suggestedPeople/UniversityIconClicked.tsx b/src/components/suggestedPeople/UniversityIconClicked.tsx
new file mode 100644
index 00000000..bde4e17f
--- /dev/null
+++ b/src/components/suggestedPeople/UniversityIconClicked.tsx
@@ -0,0 +1,61 @@
+import React from 'react';
+import {ImageStyle, StyleProp, StyleSheet, ViewProps} from 'react-native';
+import {Image, Text, View} from 'react-native-animatable';
+import {getUniversityClass, normalize} from '../../utils';
+
+export interface UniversityIconClickedProps extends ViewProps {
+ university: string;
+ university_class?: number;
+ imageStyle?: StyleProp<ImageStyle>;
+}
+
+/**
+ * Component to display university icon and class
+ */
+const UniversityIconClicked: React.FC<UniversityIconClickedProps> = ({
+ style,
+ university,
+ university_class,
+ imageStyle,
+}) => {
+ var universityIcon;
+ switch (university) {
+ case 'brown':
+ universityIcon = require('../../assets/universities/brown-clicked.png');
+ break;
+ default:
+ universityIcon = require('../../assets/universities/brown-clicked.png');
+ break;
+ }
+
+ return (
+ <View style={[styles.container, style]}>
+ <Image source={universityIcon} style={[styles.icon, imageStyle]} />
+ {university_class && (
+ <Text style={styles.univClass}>
+ {getUniversityClass(university_class)}
+ </Text>
+ )}
+ </View>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flexDirection: 'column',
+ flexWrap: 'wrap',
+ justifyContent: 'center',
+ alignItems: 'center',
+ height: '100%',
+ },
+ univClass: {
+ fontSize: normalize(14),
+ fontWeight: '500',
+ },
+ icon: {
+ width: normalize(17),
+ height: normalize(19),
+ },
+});
+
+export default UniversityIconClicked;
diff --git a/src/components/suggestedPeople/index.ts b/src/components/suggestedPeople/index.ts
index 219ee2fe..515f6fb4 100644
--- a/src/components/suggestedPeople/index.ts
+++ b/src/components/suggestedPeople/index.ts
@@ -1 +1,2 @@
export {default as MutualFriends} from './MutualFriends';
+export {default as BadgesDropdown} from './BadgesDropdown';
diff --git a/src/screens/suggestedPeople/SPBody.tsx b/src/screens/suggestedPeople/SPBody.tsx
index 8c6a3238..6572dc55 100644
--- a/src/screens/suggestedPeople/SPBody.tsx
+++ b/src/screens/suggestedPeople/SPBody.tsx
@@ -3,12 +3,9 @@ import React, {Fragment, useEffect, useMemo, useState} from 'react';
import {StyleSheet, Text, View} from 'react-native';
import {Image} from 'react-native-animatable';
import {TouchableOpacity} from 'react-native-gesture-handler';
-import LinearGradient from 'react-native-linear-gradient';
-import Animated from 'react-native-reanimated';
import RequestedButton from '../../assets/ionicons/requested-button.svg';
import {TaggsBar} from '../../components';
-import UniversityIcon from '../../components/profile/UniversityIcon';
-import {MutualFriends} from '../../components/suggestedPeople';
+import {BadgesDropdown, MutualFriends} from '../../components/suggestedPeople';
import {DATA} from '../../screens/badge/BadgeSelection';
import {
ProfilePreviewType,
@@ -17,10 +14,11 @@ import {
UniversityBadge,
} from '../../types';
import {isIPhoneX, normalize, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
+import Animated from 'react-native-reanimated';
interface SPBodyProps {
item: SuggestedPeopleDataType;
- index: number;
+ itemIndex: number;
onAddFriend: (user: ProfilePreviewType) => Promise<void>;
onCancelRequest: (user: ProfilePreviewType) => void;
loggedInUserId: string;
@@ -35,14 +33,20 @@ const SPBody: React.FC<SPBodyProps> = ({
friendship,
badges,
},
- index,
+ itemIndex,
onAddFriend,
onCancelRequest,
loggedInUserId,
}) => {
- const firstItem = index === 0;
+ const firstItem = itemIndex === 0;
const screenType = ScreenType.SuggestedPeople;
- const [localBadges, setLocalBadges] = useState([]);
+ const [localBadges, setLocalBadges] = useState<
+ {
+ badge: UniversityBadge;
+ img: string;
+ }[]
+ >([]);
+ const navigation = useNavigation();
let array = [];
useEffect(() => {
const findBadgeIcons = (badge: UniversityBadge) => {
@@ -62,7 +66,7 @@ const SPBody: React.FC<SPBodyProps> = ({
: console.log('NO BADGES FOUND');
}, []);
- const displayButton = () => {
+ const FriendButton = () => {
switch (friendship.status) {
case 'friends':
return <Fragment />;
@@ -111,7 +115,22 @@ const SPBody: React.FC<SPBodyProps> = ({
),
[suggested_people_url],
);
- const navigation = useNavigation();
+
+ const NamePlate = () => {
+ return (
+ <TouchableOpacity
+ onPress={() => {
+ navigation.push('Profile', {
+ userXId: user.id,
+ screenType,
+ });
+ }}
+ style={styles.nameInfoContainer}>
+ <Text style={styles.firstName}>{user.first_name}</Text>
+ <Text style={styles.username}>@{user.username}</Text>
+ </TouchableOpacity>
+ );
+ };
return (
<View>
@@ -119,62 +138,15 @@ const SPBody: React.FC<SPBodyProps> = ({
<View style={styles.mainContainer}>
<View style={styles.topContainer}>
<Text style={styles.title}>{firstItem && 'Suggested People'}</Text>
- <View
- style={[styles.badgesContainer, {height: 45 + 42 * badges.length}]}>
- <TouchableOpacity
- onPress={() => {
- console.log('badges: ', badges);
- }}>
- <UniversityIcon
- university="brown"
- style={styles.universityIconContainer}
- imageStyle={{width: normalize(31), height: normalize(38)}}
- />
- </TouchableOpacity>
- {localBadges &&
- localBadges.map(({badge, img}) => (
- <TouchableOpacity
- key={badge.id}
- style={styles.badgeButton}
- onPress={() => {
- navigation.navigate('MutualBadgeHolders', {
- badge_id: badge.id,
- badge_title: badge.name,
- });
- }}>
- <LinearGradient
- colors={['#4E3629', '#EC2027']}
- useAngle={true}
- angle={154.72}
- angleCenter={{x: 0.5, y: 0.5}}
- style={styles.badgeBackground}>
- <Image
- source={img}
- style={{
- width: SCREEN_WIDTH * 0.04,
- height: SCREEN_WIDTH * 0.04,
- }}
- />
- </LinearGradient>
- </TouchableOpacity>
- ))}
- </View>
+ {localBadges && (
+ <BadgesDropdown localBadges={localBadges} badges={badges} />
+ )}
</View>
<View style={styles.body}>
<View style={styles.marginManager}>
<View style={styles.addUserContainer}>
- <TouchableOpacity
- onPress={() => {
- navigation.push('Profile', {
- userXId: user.id,
- screenType,
- });
- }}
- style={styles.nameInfoContainer}>
- <Text style={styles.firstName}>{user.first_name}</Text>
- <Text style={styles.username}>@{user.username}</Text>
- </TouchableOpacity>
- {user.id !== loggedInUserId && displayButton()}
+ <NamePlate />
+ {user.id !== loggedInUserId && <FriendButton />}
</View>
</View>
<TaggsBar
@@ -209,10 +181,6 @@ const styles = StyleSheet.create({
flexDirection: 'column',
justifyContent: 'space-between',
},
- universityIconContainer: {
- width: normalize(31),
- height: normalize(38),
- },
marginManager: {marginHorizontal: '5%'},
image: {
position: 'absolute',
@@ -302,16 +270,7 @@ const styles = StyleSheet.create({
shadowOffset: {width: 2, height: 2},
shadowOpacity: 0.5,
},
- requestedButtonTitle: {
- backgroundColor: 'transparent',
- fontSize: normalize(15),
- lineHeight: normalize(18),
- fontWeight: 'bold',
- textAlign: 'center',
- letterSpacing: normalize(1),
- },
body: {},
-
button: {
justifyContent: 'center',
alignItems: 'center',
@@ -323,48 +282,6 @@ const styles = StyleSheet.create({
marginRight: '2%',
marginLeft: '1%',
},
- transparentBG: {
- backgroundColor: 'transparent',
- },
- lightBlueBG: {
- backgroundColor: '#fff',
- },
- label: {
- fontSize: normalize(15),
- fontWeight: '700',
- letterSpacing: 1,
- },
- blueLabel: {
- color: '#fff',
- },
- whiteLabel: {
- color: 'white',
- },
- badgeBackground: {
- position: 'absolute',
- width: '100%',
- height: '100%',
- borderRadius: 50,
- borderColor: 'transparent',
- borderWidth: 1,
- alignSelf: 'center',
- flexDirection: 'row',
- justifyContent: 'center',
- alignItems: 'center',
- },
- badgesContainer: {
- flexDirection: 'column',
- justifyContent: 'space-between',
- alignItems: 'center',
- width: 38,
- left: '5%',
- paddingBottom: '2%',
- },
- badgeButton: {
- width: 30,
- height: 30,
- borderRadius: 15,
- },
});
export default SPBody;
diff --git a/src/screens/suggestedPeople/SuggestedPeopleScreen.tsx b/src/screens/suggestedPeople/SuggestedPeopleScreen.tsx
index 911474cd..61adc171 100644
--- a/src/screens/suggestedPeople/SuggestedPeopleScreen.tsx
+++ b/src/screens/suggestedPeople/SuggestedPeopleScreen.tsx
@@ -1,43 +1,23 @@
import {useFocusEffect, useNavigation} from '@react-navigation/native';
-import React, {
- memo,
- Fragment,
- useCallback,
- useEffect,
- useState,
- useMemo,
- useRef,
-} from 'react';
+import React, {useCallback, useEffect, useState, useRef} from 'react';
import {
FlatList,
- ListRenderItemInfo,
RefreshControl,
StatusBar,
StyleSheet,
- Text,
- View,
ViewToken,
} from 'react-native';
-import {Image} from 'react-native-animatable';
-import {TouchableOpacity} from 'react-native-gesture-handler';
+
import Animated from 'react-native-reanimated';
import {useDispatch, useSelector, useStore} from 'react-redux';
-import {
- TabsGradient,
- TaggsBar,
- TaggLoadingIndicator,
- Background,
-} from '../../components';
-import {MutualFriends} from '../../components/suggestedPeople';
+import {TabsGradient, TaggLoadingIndicator, Background} from '../../components';
import {SP_PAGE_SIZE} from '../../constants';
import SuggestedPeopleOnboardingStackScreen from '../../routes/suggestedPeopleOnboarding/SuggestedPeopleOnboardingStackScreen';
import {getSuggestedPeople} from '../../services/SuggestedPeopleService';
import {cancelFriendRequest, resetScreenType} from '../../store/actions';
import {RootState} from '../../store/rootReducer';
-import RequestedButton from '../../assets/ionicons/requested-button.svg';
import {
FriendshipStatusType,
- FriendshipType,
ProfilePreviewType,
ScreenType,
SuggestedPeopleDataType,
@@ -234,7 +214,7 @@ const SuggestedPeopleScreen: React.FC = () => {
renderItem={(item) => {
return (
<SPBody
- index={item.index}
+ itemIndex={item.index}
item={item.item}
onAddFriend={onAddFriend}
onCancelRequest={onCancelRequest}
diff --git a/src/types/types.ts b/src/types/types.ts
index 7839e3f5..e068eeba 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -231,7 +231,7 @@ export type TypeOfNotification =
| 'MOM_FRIEND';
export type UniversityBadge = {
- id: string;
+ id: number;
name: string;
university: string;
category: string;