aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/assets/icons/plus-icon-thin.svg1
-rw-r--r--src/assets/icons/plus-icon-white.svg (renamed from src/assets/icons/plus_icon-02.svg)0
-rw-r--r--src/assets/icons/plus-icon.svg (renamed from src/assets/icons/plus_icon-01.svg)0
-rw-r--r--src/components/common/BadgeDetailView.tsx71
-rw-r--r--src/components/moments/Moment.tsx4
-rw-r--r--src/components/profile/ProfileBadges.tsx148
-rw-r--r--src/components/profile/ProfileBody.tsx3
-rw-r--r--src/components/profile/index.ts1
-rw-r--r--src/components/suggestedPeople/BadgeIcon.tsx19
-rw-r--r--src/components/suggestedPeople/legacy/BadgesDropdown.tsx13
-rw-r--r--src/constants/constants.ts2
-rw-r--r--src/screens/profile/CategorySelection.tsx2
-rw-r--r--src/screens/suggestedPeople/SPBody.tsx52
-rw-r--r--src/store/actions/user.ts23
-rw-r--r--src/store/initialStates.ts4
-rw-r--r--src/store/reducers/userReducer.ts8
-rw-r--r--src/types/types.ts16
-rw-r--r--src/utils/common.ts40
-rw-r--r--src/utils/users.ts26
19 files changed, 315 insertions, 118 deletions
diff --git a/src/assets/icons/plus-icon-thin.svg b/src/assets/icons/plus-icon-thin.svg
new file mode 100644
index 00000000..1a582474
--- /dev/null
+++ b/src/assets/icons/plus-icon-thin.svg
@@ -0,0 +1 @@
+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 216 216"><defs><style>.cls-1{fill:none;stroke-miterlimit:10;stroke-width:7.33px;}}</style></defs><circle class="cls-1" cx="108.05" cy="108.21" r="103.73" stroke="currentColor"/><rect class="cls-2" x="99.56" y="48.48" width="16.99" height="119.46" rx="7.75" fill="currentColor"/><rect class="cls-2" x="99.56" y="48.48" width="16.99" height="119.46" rx="7.75" transform="translate(-0.16 216.26) rotate(-90)" fill="currentColor"/></svg> \ No newline at end of file
diff --git a/src/assets/icons/plus_icon-02.svg b/src/assets/icons/plus-icon-white.svg
index 25527911..25527911 100644
--- a/src/assets/icons/plus_icon-02.svg
+++ b/src/assets/icons/plus-icon-white.svg
diff --git a/src/assets/icons/plus_icon-01.svg b/src/assets/icons/plus-icon.svg
index 7a3b21d2..7a3b21d2 100644
--- a/src/assets/icons/plus_icon-01.svg
+++ b/src/assets/icons/plus-icon.svg
diff --git a/src/components/common/BadgeDetailView.tsx b/src/components/common/BadgeDetailView.tsx
index 6504300c..19f1e74d 100644
--- a/src/components/common/BadgeDetailView.tsx
+++ b/src/components/common/BadgeDetailView.tsx
@@ -5,11 +5,15 @@ import {TouchableOpacity} from 'react-native-gesture-handler';
import LinearGradient from 'react-native-linear-gradient';
import {useDispatch, useSelector} from 'react-redux';
import CloseIcon from '../../assets/ionicons/close-outline.svg';
-import {BADGE_GRADIENT_FIRST} from '../../constants';
-import {BADGE_DATA} from '../../constants/badges';
+import {BADGE_GRADIENT_FIRST, BADGE_LIMIT} from '../../constants';
+import {removeUserBadge} from '../../store/actions';
import {RootState} from '../../store/rootreducer';
-import {ScreenType} from '../../types';
-import {getUniversityBadge, normalize, removeUserBadge} from '../../utils';
+import {ScreenType, UniversityBadgeDisplayType} from '../../types';
+import {
+ badgesToDisplayBadges,
+ getUniversityBadge,
+ normalize,
+} from '../../utils';
interface BadgeDetailModalProps {
userXId: string | undefined;
@@ -34,35 +38,24 @@ const BadgeDetailView: React.FC<BadgeDetailModalProps> = ({
userXId ? state.userX[screenType][userXId] : state.user,
);
const navigation = useNavigation();
- const [selectedBadgesWithImage, setSelectedBadgesWithImage] = useState<any[]>(
- [],
- );
+ const [displayBadges, setDisplayBadges] = useState<
+ UniversityBadgeDisplayType[]
+ >([]);
+ const atLimit = badges.length >= BADGE_LIMIT;
useEffect(() => {
- let badgesWithImage = [];
- badges.forEach((e) => {
- const uniData = BADGE_DATA[e.university];
- const categoryData = uniData.filter((u) => {
- return u.title === e.category;
- });
- const badgeData = categoryData[0].data.filter((c) => {
- return c.badgeName === e.name;
- });
- badgeData.forEach((c) => {
- const obj = {...e, badgeImage: c.badgeImage};
- badgesWithImage.push(obj);
- });
- });
- setTimeout(() => {
- setSelectedBadgesWithImage(badgesWithImage);
- }, 250);
+ setDisplayBadges(badgesToDisplayBadges(badges, university));
}, [badges]);
const removeBadgeCell = async (badgeName: string) => {
- await removeUserBadge(badges, badgeName, user.userId, dispatch);
+ dispatch(removeUserBadge(badgeName, user.userId));
};
- const badgeEditCell = ({item: {id, name, badgeImage}}) => {
+ const badgeEditCell = ({
+ item: {id, name, img},
+ }: {
+ item: UniversityBadgeDisplayType;
+ }) => {
return (
<TouchableOpacity
style={styles.badgeCellContainerStyles}
@@ -71,7 +64,7 @@ const BadgeDetailView: React.FC<BadgeDetailModalProps> = ({
navigation.navigate('MutualBadgeHolders', {
badge_id: id,
badge_title: name,
- badge_img: badgeImage,
+ badge_img: img,
});
}}>
<View
@@ -88,7 +81,7 @@ const BadgeDetailView: React.FC<BadgeDetailModalProps> = ({
<Image
resizeMode="cover"
style={styles.badgeImageStyles}
- source={badgeImage}
+ source={img}
/>
</LinearGradient>
{isEditable && (
@@ -121,10 +114,20 @@ const BadgeDetailView: React.FC<BadgeDetailModalProps> = ({
};
const modalHeader = () => {
- const heading = isEditable ? 'Edit your badges!' : userFullName;
- const subheading = isEditable
- ? 'Add or delete your badges'
- : 'View badges to discover groups!';
+ let heading = '';
+ let subheading = '';
+ if (isEditable) {
+ if (atLimit) {
+ heading = 'You have reached your badge limit';
+ subheading = 'Remove a badge if you wish to add more';
+ } else {
+ heading = 'Edit your badges!';
+ subheading = 'Add or delete your badges';
+ }
+ } else {
+ heading = userFullName!;
+ subheading = 'View badges to discover groups!';
+ }
return (
<View>
<Text style={styles.modalHeadingStyles}>{heading}</Text>
@@ -155,7 +158,7 @@ const BadgeDetailView: React.FC<BadgeDetailModalProps> = ({
<FlatList
contentContainerStyle={styles.modalListStyles}
scrollEnabled={false}
- data={selectedBadgesWithImage}
+ data={displayBadges}
numColumns={3}
renderItem={badgeEditCell}
keyExtractor={(item) => item.id.toString()}
@@ -228,6 +231,8 @@ const styles = StyleSheet.create({
fontSize: normalize(17),
lineHeight: normalize(20.29),
textAlign: 'center',
+ marginVertical: normalize(10),
+ marginTop: normalize(20),
},
modalSubheadingStyles: {
fontWeight: '600',
diff --git a/src/components/moments/Moment.tsx b/src/components/moments/Moment.tsx
index 0ceb8542..cde5b2e0 100644
--- a/src/components/moments/Moment.tsx
+++ b/src/components/moments/Moment.tsx
@@ -7,8 +7,8 @@ import ImagePicker from 'react-native-image-crop-picker';
import LinearGradient from 'react-native-linear-gradient';
import DeleteIcon from '../../assets/icons/delete-logo.svg';
import DownIcon from '../../assets/icons/down_icon.svg';
-import PlusIcon from '../../assets/icons/plus_icon-01.svg';
-import BigPlusIcon from '../../assets/icons/plus_icon-02.svg';
+import PlusIcon from '../../assets/icons/plus-icon.svg';
+import BigPlusIcon from '../../assets/icons/plus-icon-white.svg';
import UpIcon from '../../assets/icons/up_icon.svg';
import {TAGG_LIGHT_BLUE} from '../../constants';
import {ERROR_UPLOAD} from '../../constants/strings';
diff --git a/src/components/profile/ProfileBadges.tsx b/src/components/profile/ProfileBadges.tsx
new file mode 100644
index 00000000..8e68dc46
--- /dev/null
+++ b/src/components/profile/ProfileBadges.tsx
@@ -0,0 +1,148 @@
+import {useNavigation} from '@react-navigation/core';
+import React, {FC, useEffect, useState} from 'react';
+import {StyleSheet, Text, View} from 'react-native';
+import {ScrollView, TouchableOpacity} from 'react-native-gesture-handler';
+import {useSelector} from 'react-redux';
+import {BadgeIcon} from '..';
+import PlusIconImage from '../../assets/icons/plus-icon-thin.svg';
+import {BADGE_LIMIT} from '../../constants';
+import {RootState} from '../../store/rootReducer';
+import {ScreenType, UniversityBadgeDisplayType} from '../../types';
+import {badgesToDisplayBadges, normalize} from '../../utils';
+import BadgeDetailView from '../common/BadgeDetailView';
+
+interface ProfileBadgesProps {
+ userXId: string | undefined;
+ screenType: ScreenType;
+}
+
+const ProfileBadges: React.FC<ProfileBadgesProps> = ({userXId, screenType}) => {
+ const navigation = useNavigation();
+ const {badges, name, university} = useSelector((state: RootState) =>
+ userXId ? state.userX[screenType][userXId].profile : state.user.profile,
+ );
+ const [displayBadges, setDisplayBadges] = useState<
+ UniversityBadgeDisplayType[]
+ >([]);
+ const [isEditBadgeModalVisible, setIsEditBadgeModalVisible] = useState(false);
+ const isOwnProfile = userXId === undefined;
+
+ useEffect(() => {
+ setDisplayBadges(badgesToDisplayBadges(badges, university));
+ }, [badges]);
+
+ const PlusIcon: FC = () => (
+ <TouchableOpacity
+ onPress={() => navigation.navigate('BadgeSelection', {editing: true})}>
+ <PlusIconImage style={styles.plus} />
+ </TouchableOpacity>
+ );
+
+ const CloseIcon: FC = () => (
+ <TouchableOpacity onPress={() => setIsEditBadgeModalVisible(true)}>
+ <PlusIconImage style={styles.close} />
+ </TouchableOpacity>
+ );
+
+ return (
+ <>
+ {/* Tutorial text */}
+ {displayBadges.length === 0 && isOwnProfile && (
+ <>
+ <Text style={styles.title}>Badges</Text>
+ <Text style={styles.body}>
+ Proudly represent your team, club, or organization!
+ </Text>
+ </>
+ )}
+ {displayBadges.length === 0 && isOwnProfile && (
+ // Grey circle placeholders
+ <ScrollView
+ contentContainerStyle={styles.badgeContainer}
+ scrollEnabled={false}
+ horizontal>
+ <PlusIcon />
+ {Array(BADGE_LIMIT)
+ .fill(0)
+ .map(() => (
+ <View style={[styles.grey, styles.circle]} />
+ ))}
+ </ScrollView>
+ )}
+ {displayBadges.length !== 0 && (
+ // Populating actual badges
+ <ScrollView
+ contentContainerStyle={styles.badgeContainer}
+ scrollEnabled={false}
+ horizontal>
+ {/* Actual badges */}
+ {displayBadges.map((displayBadge) => (
+ <BadgeIcon key={displayBadge.id} badge={displayBadge} />
+ ))}
+ {/* Plus icon */}
+ {displayBadges.length < BADGE_LIMIT && isOwnProfile && <PlusIcon />}
+ {/* Empty placeholders for space-between styling */}
+ {Array(BADGE_LIMIT + 1)
+ .fill(0)
+ .splice(displayBadges.length + 1, BADGE_LIMIT)
+ .map(() => (
+ <View style={styles.circle} />
+ ))}
+ {/* X button */}
+ {displayBadges.length === BADGE_LIMIT && isOwnProfile && (
+ <CloseIcon />
+ )}
+ </ScrollView>
+ )}
+ {isEditBadgeModalVisible && (
+ <BadgeDetailView
+ userXId={userXId}
+ screenType={screenType}
+ isEditable={isOwnProfile}
+ userFullName={name}
+ setBadgeViewVisible={setIsEditBadgeModalVisible}
+ />
+ )}
+ </>
+ );
+};
+
+const styles = StyleSheet.create({
+ title: {
+ fontWeight: '600',
+ fontSize: normalize(13.5),
+ lineHeight: normalize(18),
+ },
+ body: {
+ fontSize: normalize(13.5),
+ lineHeight: normalize(17),
+ marginBottom: 10,
+ },
+ badgeContainer: {
+ width: '100%',
+ justifyContent: 'space-between',
+ marginTop: 10,
+ marginBottom: 15,
+ },
+ circle: {
+ width: normalize(31),
+ height: normalize(31),
+ borderRadius: normalize(31) / 2,
+ },
+ grey: {
+ backgroundColor: '#c4c4c4',
+ },
+ plus: {
+ width: normalize(31),
+ height: normalize(31),
+ color: 'black',
+ },
+ close: {
+ width: normalize(31),
+ height: normalize(31),
+ color: 'grey',
+ transform: [{rotate: '45deg'}],
+ },
+});
+
+export default ProfileBadges;
diff --git a/src/components/profile/ProfileBody.tsx b/src/components/profile/ProfileBody.tsx
index 8743acfb..c0ee508a 100644
--- a/src/components/profile/ProfileBody.tsx
+++ b/src/components/profile/ProfileBody.tsx
@@ -20,6 +20,7 @@ import {
import {canViewProfile} from '../../utils/users';
import {FriendsButton} from '../common';
import {MessageButton} from '../messages';
+import ProfileBadges from './ProfileBadges';
import ToggleButton from './ToggleButton';
interface ProfileBodyProps {
@@ -65,6 +66,7 @@ const ProfileBody: React.FC<ProfileBodyProps> = ({
return (
<View onLayout={onLayout} style={styles.container}>
+ <ProfileBadges {...{userXId, screenType}} />
<Text style={styles.username}>{`@${username}`}</Text>
{biography.length > 0 && (
<Text style={styles.biography}>{`${biography}`}</Text>
@@ -137,7 +139,6 @@ const styles = StyleSheet.create({
justifyContent: 'space-between',
},
container: {
- paddingVertical: '1%',
paddingHorizontal: 18,
backgroundColor: 'white',
},
diff --git a/src/components/profile/index.ts b/src/components/profile/index.ts
index c544c3f2..faf273d9 100644
--- a/src/components/profile/index.ts
+++ b/src/components/profile/index.ts
@@ -9,3 +9,4 @@ export {default as ProfileMoreInfoDrawer} from './ProfileMoreInfoDrawer';
export {default as MomentMoreInfoDrawer} from './MomentMoreInfoDrawer';
export {default as UniversityIcon} from './UniversityIcon';
export {default as TaggAvatar} from './TaggAvatar';
+export {default as ProfileBadges} from './ProfileBadges';
diff --git a/src/components/suggestedPeople/BadgeIcon.tsx b/src/components/suggestedPeople/BadgeIcon.tsx
index 8f576a43..616bac93 100644
--- a/src/components/suggestedPeople/BadgeIcon.tsx
+++ b/src/components/suggestedPeople/BadgeIcon.tsx
@@ -1,24 +1,17 @@
import {useNavigation} from '@react-navigation/core';
import React from 'react';
-import {
- Image,
- ImageSourcePropType,
- StyleProp,
- StyleSheet,
- ViewStyle,
-} from 'react-native';
+import {Image, StyleProp, StyleSheet, ViewStyle} from 'react-native';
import {TouchableOpacity} from 'react-native-gesture-handler';
import LinearGradient from 'react-native-linear-gradient';
-import {UniversityBadge} from '../../types';
+import {UniversityBadgeDisplayType} from '../../types';
import {normalize} from '../../utils';
interface BadgeIconProps {
- badge: UniversityBadge;
- img: ImageSourcePropType;
+ badge: UniversityBadgeDisplayType;
style?: StyleProp<ViewStyle>;
}
-const BadgeIcon: React.FC<BadgeIconProps> = ({badge, img, style}) => {
+const BadgeIcon: React.FC<BadgeIconProps> = ({badge, style}) => {
const navigation = useNavigation();
return (
<TouchableOpacity
@@ -27,7 +20,7 @@ const BadgeIcon: React.FC<BadgeIconProps> = ({badge, img, style}) => {
navigation.navigate('MutualBadgeHolders', {
badge_id: badge.id,
badge_title: badge.name,
- badge_img: img,
+ badge_img: badge.img,
});
}}>
<LinearGradient
@@ -36,7 +29,7 @@ const BadgeIcon: React.FC<BadgeIconProps> = ({badge, img, style}) => {
angle={154.72}
angleCenter={{x: 0.5, y: 0.5}}
style={styles.badgeBackground}>
- <Image source={img} style={styles.icon} />
+ <Image source={badge.img} style={styles.icon} />
</LinearGradient>
</TouchableOpacity>
);
diff --git a/src/components/suggestedPeople/legacy/BadgesDropdown.tsx b/src/components/suggestedPeople/legacy/BadgesDropdown.tsx
index 267355f3..2c177e69 100644
--- a/src/components/suggestedPeople/legacy/BadgesDropdown.tsx
+++ b/src/components/suggestedPeople/legacy/BadgesDropdown.tsx
@@ -1,18 +1,15 @@
import React, {useEffect, useState} from 'react';
-import {ImageSourcePropType, StyleSheet} from 'react-native';
+import {StyleSheet} from 'react-native';
import {TouchableOpacity} from 'react-native-gesture-handler';
import Animated, {Easing} from 'react-native-reanimated';
import {BadgeIcon, UniversityIcon} from '../..';
-import {UniversityBadge, UniversityType} from '../../../types';
+import {UniversityBadgeDisplayType, UniversityType} from '../../../types';
import {normalize} from '../../../utils';
import UniversityIconClicked from '../UniversityIconClicked';
interface BadgesDropdownProps {
university: UniversityType;
- localBadges: {
- badge: UniversityBadge;
- img: ImageSourcePropType;
- }[];
+ localBadges: UniversityBadgeDisplayType[];
}
const BadgesDropdown: React.FC<BadgesDropdownProps> = ({
@@ -92,7 +89,7 @@ const BadgesDropdown: React.FC<BadgesDropdownProps> = ({
)}
</TouchableOpacity>
{localBadges &&
- localBadges.map(({badge, img}, index) => (
+ localBadges.map((badge, index) => (
<Animated.View
key={badge.id}
style={[
@@ -102,7 +99,7 @@ const BadgesDropdown: React.FC<BadgesDropdownProps> = ({
zIndex: -1 * badge.id,
},
]}>
- <BadgeIcon badge={badge} img={img} />
+ <BadgeIcon badge={badge} />
</Animated.View>
))}
</Animated.View>
diff --git a/src/constants/constants.ts b/src/constants/constants.ts
index 99d3901b..a6d98883 100644
--- a/src/constants/constants.ts
+++ b/src/constants/constants.ts
@@ -21,6 +21,8 @@ export const AVATAR_GRADIENT_DIM = 50;
export const TAGG_ICON_DIM = 58;
export const TAGG_RING_DIM = normalize(60);
+export const BADGE_LIMIT = 5;
+
export const INTEGRATED_SOCIAL_LIST: string[] = [
'Instagram',
'Facebook',
diff --git a/src/screens/profile/CategorySelection.tsx b/src/screens/profile/CategorySelection.tsx
index c02eef0d..ea443fce 100644
--- a/src/screens/profile/CategorySelection.tsx
+++ b/src/screens/profile/CategorySelection.tsx
@@ -11,7 +11,7 @@ import {
} from 'react-native';
import {ScrollView} from 'react-native-gesture-handler';
import {useDispatch, useSelector} from 'react-redux';
-import PlusIcon from '../../assets/icons/plus_icon-01.svg';
+import PlusIcon from '../../assets/icons/plus-icon.svg';
import {Background, MomentCategory} from '../../components';
import {MOMENT_CATEGORIES, TAGG_LIGHT_BLUE_2} from '../../constants';
import {ERROR_SOMETHING_WENT_WRONG} from '../../constants/strings';
diff --git a/src/screens/suggestedPeople/SPBody.tsx b/src/screens/suggestedPeople/SPBody.tsx
index eb80da49..fea67950 100644
--- a/src/screens/suggestedPeople/SPBody.tsx
+++ b/src/screens/suggestedPeople/SPBody.tsx
@@ -1,19 +1,24 @@
import {useNavigation} from '@react-navigation/native';
import React, {Fragment, useEffect, useMemo, useState} from 'react';
-import {ImageSourcePropType, StyleSheet, Text, View} from 'react-native';
+import {StyleSheet, Text, View} from 'react-native';
import {Image} from 'react-native-animatable';
import {TouchableOpacity} from 'react-native-gesture-handler';
import RequestedButton from '../../assets/ionicons/requested-button.svg';
import {UniversityIcon} from '../../components';
import {BadgeIcon, MutualFriends} from '../../components/suggestedPeople';
-import {BADGE_DATA} from '../../constants/badges';
import {
ProfilePreviewType,
ScreenType,
SuggestedPeopleDataType,
- UniversityBadge,
+ UniversityBadgeDisplayType,
} from '../../types';
-import {isIPhoneX, normalize, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
+import {
+ badgesToDisplayBadges,
+ isIPhoneX,
+ normalize,
+ SCREEN_HEIGHT,
+ SCREEN_WIDTH,
+} from '../../utils';
interface SPBodyProps {
item: SuggestedPeopleDataType;
@@ -39,30 +44,12 @@ const SPBody: React.FC<SPBodyProps> = ({
}) => {
const firstItem = itemIndex === 0;
const screenType = ScreenType.SuggestedPeople;
- const [localBadges, setLocalBadges] = useState<
- {
- badge: UniversityBadge;
- img: ImageSourcePropType;
- }[]
+ const [displayBadges, setDisplayBadges] = useState<
+ UniversityBadgeDisplayType[]
>([]);
const navigation = useNavigation();
useEffect(() => {
- const newBadges: {badge: UniversityBadge; img: any}[] = [];
- const findBadgeIcons = (badge: UniversityBadge) => {
- BADGE_DATA[university]?.forEach((item) => {
- if (item.title === badge.category) {
- item.data.forEach((object) => {
- if (object.badgeName === badge.name) {
- newBadges.push({badge, img: object.badgeImage});
- }
- });
- }
- });
- setLocalBadges(newBadges);
- };
- badges
- ? badges.forEach((badge) => findBadgeIcons(badge))
- : console.log('NO BADGES FOUND');
+ setDisplayBadges(badgesToDisplayBadges(badges, university));
}, []);
const FriendButton = () => {
@@ -131,12 +118,15 @@ const SPBody: React.FC<SPBodyProps> = ({
const Badges = () => (
// Badges aligned left and spaced as if there are 5 items
<View style={styles.badgeContainer}>
- {localBadges.map(({badge, img}, index) => (
- <BadgeIcon key={index} badge={badge} img={img} style={styles.badge} />
- ))}
- {[0, 0, 0, 0, 0].splice(localBadges.length, 5).map((_, index) => (
- <View key={index} style={styles.badge} />
+ {displayBadges.map((displayBadge, index) => (
+ <BadgeIcon key={index} badge={displayBadge} style={styles.badge} />
))}
+ {Array(5)
+ .fill(0)
+ .splice(displayBadges.length, 5)
+ .map((_, index) => (
+ <View key={index} style={styles.badge} />
+ ))}
</View>
);
@@ -159,7 +149,7 @@ const SPBody: React.FC<SPBodyProps> = ({
{user.id !== loggedInUserId && <FriendButton />}
</View>
</View>
- {localBadges.length !== 0 && <Badges />}
+ {displayBadges.length !== 0 && <Badges />}
<View style={styles.marginManager}>
<MutualFriends user={user} friends={mutual_friends} />
</View>
diff --git a/src/store/actions/user.ts b/src/store/actions/user.ts
index 941101df..b1cb8719 100644
--- a/src/store/actions/user.ts
+++ b/src/store/actions/user.ts
@@ -4,12 +4,14 @@ import {Action, ThunkAction} from '@reduxjs/toolkit';
import {
getProfilePic,
loadProfileInfo,
+ removeBadgesService,
sendSuggestedPeopleLinked,
} from '../../services';
import {UniversityBadge, UserType} from '../../types/types';
import {getTokenOrLogout} from '../../utils';
import {
clearHeaderAndProfileImages,
+ profileBadgeRemoved,
profileBadgesUpdated,
profileCompletionStageUpdated,
setIsOnboardedUser,
@@ -107,6 +109,27 @@ export const updateUserBadges =
}
};
+/**
+ * Removes a single badge from logged-in user by badge name.
+ * @param badgeName name of badge to be removed
+ * @param loggedInUserId userId of loggedInUser
+ */
+export const removeUserBadge =
+ (
+ badgeName: string,
+ loggedInUserId: string,
+ ): ThunkAction<Promise<void>, RootState, unknown, Action<string>> =>
+ async (dispatch) => {
+ try {
+ const success = await removeBadgesService([badgeName], loggedInUserId);
+ if (success) {
+ dispatch({type: profileBadgeRemoved.type, payload: {badge: badgeName}});
+ }
+ } catch (error) {
+ console.log(error);
+ }
+ };
+
export const updateProfileCompletionStage =
(
stage: number,
diff --git a/src/store/initialStates.ts b/src/store/initialStates.ts
index e0f9d776..e2902a2d 100644
--- a/src/store/initialStates.ts
+++ b/src/store/initialStates.ts
@@ -1,4 +1,4 @@
-import {CommentThreadType} from './../types/types';
+import {CommentThreadType, UniversityType} from './../types/types';
import {
MomentType,
NotificationType,
@@ -17,7 +17,7 @@ export const NO_PROFILE: ProfileInfoType = {
gender: '',
birthday: undefined,
university_class: 2021,
- university: undefined,
+ university: UniversityType.Empty,
badges: [],
//Default to an invalid value and ignore it gracefully while showing tutorials / popups.
profile_completion_stage: -1,
diff --git a/src/store/reducers/userReducer.ts b/src/store/reducers/userReducer.ts
index 97bf845c..4692c5d3 100644
--- a/src/store/reducers/userReducer.ts
+++ b/src/store/reducers/userReducer.ts
@@ -46,6 +46,12 @@ const userDataSlice = createSlice({
state.profile.badges = action.payload.badges;
},
+ profileBadgeRemoved: (state, action) => {
+ state.profile.badges = state.profile.badges.filter(
+ (badge) => badge.name !== action.payload.badge,
+ );
+ },
+
profileCompletionStageUpdated: (state, action) => {
state.profile.profile_completion_stage = action.payload.stage;
},
@@ -95,6 +101,6 @@ export const {
setSuggestedPeopleImage,
clearHeaderAndProfileImages,
profileBadgesUpdated,
- // setChatClientReady,
+ profileBadgeRemoved,
} = userDataSlice.actions;
export const userDataReducer = userDataSlice.reducer;
diff --git a/src/types/types.ts b/src/types/types.ts
index b294e3f1..e54c2201 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -1,3 +1,4 @@
+import {ImageSourcePropType} from 'react-native';
import Animated from 'react-native-reanimated';
import {Channel as ChannelType, StreamChat} from 'stream-chat';
@@ -262,6 +263,10 @@ export type UniversityBadge = {
category: string;
};
+export interface UniversityBadgeDisplayType extends UniversityBadge {
+ img: ImageSourcePropType;
+}
+
export type SuggestedPeopleDataType = {
user: ProfilePreviewType;
university: UniversityType;
@@ -291,7 +296,16 @@ export type ContactType = {
};
export type UniversityBadgeType = 'Search' | 'Crest';
-export type BadgeDataType = Record<UniversityType, any[]>;
+export type BadgeDataType = Record<
+ UniversityType,
+ {
+ title: string;
+ data: {
+ badgeName: string;
+ badgeImage: ImageSourcePropType;
+ }[];
+ }[]
+>;
// Stream Chat Types
export type LocalAttachmentType = Record<string, unknown>;
diff --git a/src/utils/common.ts b/src/utils/common.ts
index 95e77f64..cfd9244a 100644
--- a/src/utils/common.ts
+++ b/src/utils/common.ts
@@ -1,11 +1,17 @@
import AsyncStorage from '@react-native-community/async-storage';
import moment from 'moment';
-import {Linking} from 'react-native';
+import {ImageSourcePropType, Linking} from 'react-native';
import {getAll} from 'react-native-contacts';
-import {BROWSABLE_SOCIAL_URLS, TOGGLE_BUTTON_TYPE} from '../constants';
+import {
+ BADGE_DATA,
+ BROWSABLE_SOCIAL_URLS,
+ TOGGLE_BUTTON_TYPE,
+} from '../constants';
import {
ContactType,
NotificationType,
+ UniversityBadge,
+ UniversityBadgeDisplayType,
UniversityBadgeType,
UniversityType,
} from './../types/types';
@@ -197,3 +203,33 @@ export const validateImageLink = async (url: string | undefined) => {
return false;
});
};
+
+/**
+ * Turns a list of badges into display badges (just a badge with img) by
+ * looking up the img source from our badge asset lookup constant.
+ *
+ * WARNING: Assumes a small list of badges, complexity goes up exponentially.
+ *
+ * @param badges list of university badges
+ * @param university university of which all the badges belong
+ * @returns list of display badges
+ */
+export const badgesToDisplayBadges = (
+ badges: UniversityBadge[],
+ university: UniversityType,
+) => {
+ const badgeSet: Set<string> = new Set(badges.map((b) => b.category + b.name));
+ const badgeToImgMap: Record<string, ImageSourcePropType> = {};
+ BADGE_DATA[university].forEach((category) => {
+ category.data.forEach((badgeInfo) => {
+ const key = category.title + badgeInfo.badgeName;
+ if (badgeSet.has(key)) {
+ badgeToImgMap[key] = badgeInfo.badgeImage;
+ }
+ });
+ });
+ return <UniversityBadgeDisplayType[]>badges.map((b) => ({
+ ...b,
+ img: badgeToImgMap[b.category + b.name],
+ }));
+};
diff --git a/src/utils/users.ts b/src/utils/users.ts
index 8505cde2..64ad10e9 100644
--- a/src/utils/users.ts
+++ b/src/utils/users.ts
@@ -1,7 +1,8 @@
-import {Alert} from 'react-native';
import AsyncStorage from '@react-native-community/async-storage';
+import {Alert} from 'react-native';
+import ImagePicker from 'react-native-image-crop-picker';
import {INTEGRATED_SOCIAL_LIST} from '../constants';
-import {isUserBlocked, loadSocialPosts, removeBadgesService} from '../services';
+import {isUserBlocked, loadSocialPosts, patchEditProfile} from '../services';
import {
loadAllSocials,
loadBlockedList,
@@ -11,7 +12,6 @@ import {
loadUserMoments,
loadUserNotifications,
logout,
- updateUserBadges,
} from '../store/actions';
import {NO_SOCIAL_ACCOUNTS} from '../store/initialStates';
import {loadUserMomentCategories} from './../store/actions/momentCategories';
@@ -23,10 +23,7 @@ import {
ProfilePreviewType,
ScreenType,
UserType,
- UniversityBadge,
} from './../types/types';
-import ImagePicker from 'react-native-image-crop-picker';
-import {patchEditProfile} from '../services';
const loadData = async (dispatch: AppDispatch, user: UserType) => {
await Promise.all([
@@ -205,23 +202,6 @@ export const canViewProfile = (
return false;
};
-/* Function to call remove badge service,
- * remove selected badge from list passed in and
- * dispatch thunk action to update store
- */
-export const removeUserBadge = async (
- badges: UniversityBadge[],
- badgeName: string,
- userId: string,
- dispatch: AppDispatch,
-) => {
- const success = await removeBadgesService([badgeName], userId);
- if (success === true) {
- badges = badges.filter((badge) => badge.name !== badgeName);
- dispatch(updateUserBadges(badges));
- }
-};
-
export const navigateToProfile = async (
state: RootState,
dispatch: any,