aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/common/BadgeDetailView.tsx89
-rw-r--r--src/components/notifications/Notification.tsx49
-rw-r--r--src/constants/api.ts1
-rw-r--r--src/screens/badge/BadgeSelection.tsx47
-rw-r--r--src/services/SuggestedPeopleService.ts44
-rw-r--r--src/store/actions/user.ts22
-rw-r--r--src/store/initialStates.ts1
-rw-r--r--src/store/reducers/userReducer.ts5
-rw-r--r--src/types/types.ts1
-rw-r--r--src/utils/moments.ts34
-rw-r--r--src/utils/users.ts21
11 files changed, 217 insertions, 97 deletions
diff --git a/src/components/common/BadgeDetailView.tsx b/src/components/common/BadgeDetailView.tsx
index bc4384e8..6504300c 100644
--- a/src/components/common/BadgeDetailView.tsx
+++ b/src/components/common/BadgeDetailView.tsx
@@ -1,24 +1,15 @@
import {useNavigation} from '@react-navigation/core';
import React, {useEffect, useState} from 'react';
-import {
- ActivityIndicator,
- FlatList,
- Image,
- Modal,
- StyleSheet,
- Text,
- View,
-} from 'react-native';
+import {FlatList, Image, Modal, StyleSheet, Text, View} from 'react-native';
import {TouchableOpacity} from 'react-native-gesture-handler';
import LinearGradient from 'react-native-linear-gradient';
-import {useSelector} from 'react-redux';
+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 {getSuggestedPeopleProfile, removeBadgesService} from '../../services';
import {RootState} from '../../store/rootreducer';
-import {ScreenType, UniversityBadge} from '../../types';
-import {getUniversityBadge, normalize} from '../../utils';
+import {ScreenType} from '../../types';
+import {getUniversityBadge, normalize, removeUserBadge} from '../../utils';
interface BadgeDetailModalProps {
userXId: string | undefined;
@@ -35,38 +26,21 @@ const BadgeDetailView: React.FC<BadgeDetailModalProps> = ({
isEditable = true,
setBadgeViewVisible,
}) => {
- const {user, profile} = useSelector((state: RootState) =>
+ const dispatch = useDispatch();
+ const {
+ user,
+ profile: {university, badges},
+ } = useSelector((state: RootState) =>
userXId ? state.userX[screenType][userXId] : state.user,
);
const navigation = useNavigation();
- const [selectedBadges, setSelectedBadges] = useState<UniversityBadge[]>([]);
- const [isLoading, setIsLoading] = useState(true);
const [selectedBadgesWithImage, setSelectedBadgesWithImage] = useState<any[]>(
[],
);
- const fetchBadges = async () => {
- if (user.userId) {
- const response = await getSuggestedPeopleProfile(user.userId);
- if (response) {
- const data = response.badges;
- let extractedBadgeNames: UniversityBadge[] = [];
- data.forEach((badge) => {
- extractedBadgeNames.push(badge);
- });
- setSelectedBadges(extractedBadgeNames);
- }
- }
- };
-
- useEffect(() => {
- setIsLoading(true);
- fetchBadges();
- }, []);
-
useEffect(() => {
let badgesWithImage = [];
- selectedBadges.forEach((e) => {
+ badges.forEach((e) => {
const uniData = BADGE_DATA[e.university];
const categoryData = uniData.filter((u) => {
return u.title === e.category;
@@ -81,14 +55,11 @@ const BadgeDetailView: React.FC<BadgeDetailModalProps> = ({
});
setTimeout(() => {
setSelectedBadgesWithImage(badgesWithImage);
- setIsLoading(false);
}, 250);
- }, [selectedBadges]);
+ }, [badges]);
- const removeBadgeCell = async (badge: string) => {
- setIsLoading(true);
- await removeBadgesService([badge], user.userId);
- fetchBadges();
+ const removeBadgeCell = async (badgeName: string) => {
+ await removeUserBadge(badges, badgeName, user.userId, dispatch);
};
const badgeEditCell = ({item: {id, name, badgeImage}}) => {
@@ -175,38 +146,27 @@ const BadgeDetailView: React.FC<BadgeDetailModalProps> = ({
<View style={styles.modalImageContainerStyles}>
<Image
style={styles.modalImageStyles}
- source={getUniversityBadge(profile.university, 'Crest')}
+ source={getUniversityBadge(university, 'Crest')}
/>
</View>
</View>
{modalHeader()}
- {!isLoading && (
- <View>
- <FlatList
- contentContainerStyle={styles.modalListStyles}
- scrollEnabled={false}
- data={selectedBadgesWithImage}
- numColumns={3}
- renderItem={badgeEditCell}
- keyExtractor={(item) => item.id.toString()}
- />
- </View>
- )}
- {isLoading && _loaderView()}
+ <View>
+ <FlatList
+ contentContainerStyle={styles.modalListStyles}
+ scrollEnabled={false}
+ data={selectedBadgesWithImage}
+ numColumns={3}
+ renderItem={badgeEditCell}
+ keyExtractor={(item) => item.id.toString()}
+ />
+ </View>
{isEditable && addButton()}
</View>
</View>
);
};
- const _loaderView = () => {
- return (
- <View style={styles.loaderStyles}>
- <ActivityIndicator animating={isLoading} size="large" color="black" />
- </View>
- );
- };
-
return (
<Modal
animationType="fade"
@@ -269,7 +229,6 @@ const styles = StyleSheet.create({
lineHeight: normalize(20.29),
textAlign: 'center',
},
- loaderStyles: {justifyContent: 'center', marginVertical: 20},
modalSubheadingStyles: {
fontWeight: '600',
fontSize: normalize(11),
diff --git a/src/components/notifications/Notification.tsx b/src/components/notifications/Notification.tsx
index a74480b4..ae884b42 100644
--- a/src/components/notifications/Notification.tsx
+++ b/src/components/notifications/Notification.tsx
@@ -26,6 +26,8 @@ import {
} from '../../types';
import {
fetchUserX,
+ getTimeInShorthand,
+ normalize,
SCREEN_HEIGHT,
SCREEN_WIDTH,
userXInStore,
@@ -47,6 +49,7 @@ const Notification: React.FC<NotificationProps> = (props) => {
notification_type,
notification_object,
unread,
+ timestamp,
},
screenType,
loggedInUser,
@@ -231,7 +234,12 @@ const Notification: React.FC<NotificationProps> = (props) => {
{notification_type === 'SYSTEM_MSG' ? (
// Only verbage
<View style={styles.contentContainer}>
- <Text style={styles.actorName}>{verbage}</Text>
+ <View style={styles.textContainerStyles}>
+ <Text style={styles.actorName}>{verbage}</Text>
+ <Text style={styles.timeStampStyles}>
+ {getTimeInShorthand(timestamp)}
+ </Text>
+ </View>
</View>
) : (
<>
@@ -242,8 +250,17 @@ const Notification: React.FC<NotificationProps> = (props) => {
{first_name} {last_name}
</Text>
</TouchableWithoutFeedback>
- <TouchableWithoutFeedback onPress={onNotificationTap}>
- <Text>{verbage}</Text>
+ <TouchableWithoutFeedback
+ style={styles.textContainerStyles}
+ onPress={onNotificationTap}>
+ <Text style={styles.verbageStyles}>
+ {verbage}
+ <Text style={styles.timeStampStyles}>
+ {' '}
+ {getTimeInShorthand(timestamp)}
+ </Text>
+ </Text>
+ {/* <Text style={styles.verbageStyles}>{verbage}</Text> */}
</TouchableWithoutFeedback>
</View>
{/* Friend request accept/decline button */}
@@ -304,22 +321,40 @@ const styles = StyleSheet.create({
contentContainer: {
flex: 5,
marginLeft: '5%',
+ marginRight: '3%',
height: '80%',
flexDirection: 'column',
justifyContent: 'space-around',
},
actorName: {
- fontSize: 15,
+ fontSize: normalize(12),
fontWeight: '700',
+ lineHeight: normalize(14.32),
},
moment: {
- height: 42,
- width: 42,
- right: '5%',
+ height: normalize(42),
+ width: normalize(42),
},
buttonsContainer: {
height: '80%',
},
+ textContainerStyles: {
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ },
+ verbageStyles: {
+ fontWeight: '500',
+ fontSize: normalize(11),
+ lineHeight: normalize(13.13),
+ },
+ timeStampStyles: {
+ fontWeight: '700',
+ fontSize: normalize(12),
+ lineHeight: normalize(14.32),
+ marginHorizontal: 2,
+ color: '#828282',
+ textAlignVertical: 'center',
+ },
imageFlex: {
flex: 1,
},
diff --git a/src/constants/api.ts b/src/constants/api.ts
index 45b6e8ae..d45616a1 100644
--- a/src/constants/api.ts
+++ b/src/constants/api.ts
@@ -60,6 +60,7 @@ export const UPDATE_BADGES_ENDPOINT: string =
SP_USERS_ENDPOINT + 'update_badges/';
export const REMOVE_BADGES_ENDPOINT: string =
SP_USERS_ENDPOINT + 'remove_badges/';
+export const GET_USER_BADGES_ENDPOINT: string = SP_USERS_ENDPOINT + 'get_badges/';
// Register as FCM device
export const FCM_ENDPOINT: string = API_URL + 'fcm/';
diff --git a/src/screens/badge/BadgeSelection.tsx b/src/screens/badge/BadgeSelection.tsx
index 38a2b01c..d0dcfa4c 100644
--- a/src/screens/badge/BadgeSelection.tsx
+++ b/src/screens/badge/BadgeSelection.tsx
@@ -8,14 +8,20 @@ import LinearGradient from 'react-native-linear-gradient';
import {useDispatch, useSelector} from 'react-redux';
import {BACKGROUND_GRADIENT_MAP} from '../../constants';
import {BADGE_DATA} from '../../constants/badges';
-import {ERROR_BADGES_EXCEED_LIMIT} from '../../constants/strings';
+import {
+ ERROR_BADGES_EXCEED_LIMIT,
+ SUCCESS_BADGES_UPDATE,
+} from '../../constants/strings';
import {MainStackParams} from '../../routes';
import {
addBadgesService,
- getSuggestedPeopleProfile,
+ getBadgesService,
updateBadgesService,
} from '../../services';
-import {suggestedPeopleBadgesFinished} from '../../store/actions';
+import {
+ suggestedPeopleBadgesFinished,
+ updateUserBadges,
+} from '../../store/actions';
import {RootState} from '../../store/rootReducer';
import {BackgroundGradientType} from '../../types';
import {SCREEN_HEIGHT, StatusBarHeight} from '../../utils';
@@ -36,37 +42,47 @@ const BadgeSelection: React.FC<BadgeSelectionProps> = ({route}) => {
const {editing} = route.params;
const {
user: {userId: loggedInUserId},
- profile: {university},
+ profile: {university, badges},
} = useSelector((state: RootState) => state.user);
const [selectedBadges, setSelectedBadges] = useState<string[]>([]);
const dispatch = useDispatch();
const navigation = useNavigation();
- // Loading badges data and extracting into a string []
+ // Extracting badges data into a string []
useEffect(() => {
const loadData = async () => {
- const response = await getSuggestedPeopleProfile(loggedInUserId);
- if (response) {
- const data = response.badges;
- let extractedBadgeNames: string[] = [];
- data.forEach((badge) => {
- extractedBadgeNames.push(badge.name);
- });
- setSelectedBadges(extractedBadgeNames);
- }
+ let extractedBadgeNames: string[] = [];
+ badges.forEach((badge) => {
+ extractedBadgeNames.push(badge.name);
+ });
+ setSelectedBadges(extractedBadgeNames);
};
if (editing) {
loadData();
}
}, []);
+ // Retrieve updated badges using get badges service and udpate the store
+ const loadUserBadges = async () => {
+ const newBadges = await getBadgesService(loggedInUserId);
+ dispatch(updateUserBadges(newBadges));
+ };
+
navigation.setOptions({
headerRight: () => (
<TouchableOpacity
style={styles.rightButtonContainer}
onPress={async () => {
if (editing) {
- await updateBadgesService(selectedBadges, university);
+ const success = await updateBadgesService(
+ selectedBadges,
+ university,
+ );
+ if (success === true) {
+ // Load updated badges to store
+ loadUserBadges();
+ Alert.alert(SUCCESS_BADGES_UPDATE);
+ }
if (navigation.canGoBack()) {
navigation.goBack();
} else {
@@ -82,6 +98,7 @@ const BadgeSelection: React.FC<BadgeSelectionProps> = ({route}) => {
);
if (success) {
dispatch(suggestedPeopleBadgesFinished());
+ loadUserBadges();
navigation.navigate('SuggestedPeople');
}
} else {
diff --git a/src/services/SuggestedPeopleService.ts b/src/services/SuggestedPeopleService.ts
index 7f5b4b8c..2ae8cf55 100644
--- a/src/services/SuggestedPeopleService.ts
+++ b/src/services/SuggestedPeopleService.ts
@@ -2,18 +2,22 @@ import AsyncStorage from '@react-native-community/async-storage';
import {
ERROR_BADGES_EXCEED_LIMIT,
ERROR_UPLOAD_BADGES,
- SUCCESS_BADGES_UPDATE,
} from '../constants/strings';
import {
ADD_BADGES_ENDPOINT,
EDIT_PROFILE_ENDPOINT,
+ GET_USER_BADGES_ENDPOINT,
REMOVE_BADGES_ENDPOINT,
SP_MUTUAL_BADGE_HOLDERS_ENDPOINT,
SP_UPDATE_PICTURE_ENDPOINT,
SP_USERS_ENDPOINT,
UPDATE_BADGES_ENDPOINT,
} from '../constants/api';
-import {ProfilePreviewType, SuggestedPeopleDataType} from '../types';
+import {
+ ProfilePreviewType,
+ SuggestedPeopleDataType,
+ UniversityBadge,
+} from '../types';
import {Alert} from 'react-native';
export const sendSuggestedPeopleLinked = async (
@@ -166,6 +170,31 @@ export const addBadgesService = async (
}
};
+export const getBadgesService = async (userId: string) => {
+ try {
+ const token = await AsyncStorage.getItem('token');
+ const response = await fetch(
+ GET_USER_BADGES_ENDPOINT + '?user_id=' + userId,
+ {
+ method: 'GET',
+ headers: {
+ Authorization: 'Token ' + token,
+ },
+ },
+ );
+ if (response.status === 200) {
+ const data: UniversityBadge[] = await response.json();
+ return data ? data : [];
+ } else {
+ console.log('Error loading badges data');
+ return [];
+ }
+ } catch (error) {
+ console.log('Exception occued while loading badges data, ', error);
+ return [];
+ }
+};
+
export const updateBadgesService = async (
selectedBadges: string[],
university: string,
@@ -185,11 +214,10 @@ export const updateBadgesService = async (
});
if (response.status === 400) {
Alert.alert(ERROR_BADGES_EXCEED_LIMIT);
- return;
+ return false;
}
if (response.status === 200) {
- Alert.alert(SUCCESS_BADGES_UPDATE);
- return;
+ return true;
}
} catch (error) {
console.log(error);
@@ -216,14 +244,14 @@ export const removeBadgesService = async (
});
if (response.status === 400) {
Alert.alert(ERROR_BADGES_EXCEED_LIMIT);
- return;
+ return false;
}
if (response.status === 200) {
- Alert.alert(SUCCESS_BADGES_UPDATE);
- return;
+ return true;
}
} catch (error) {
console.log(error);
Alert.alert(ERROR_UPLOAD_BADGES);
+ return false;
}
};
diff --git a/src/store/actions/user.ts b/src/store/actions/user.ts
index 3ebd4190..96e636f6 100644
--- a/src/store/actions/user.ts
+++ b/src/store/actions/user.ts
@@ -6,10 +6,11 @@ import {
loadProfileInfo,
sendSuggestedPeopleLinked,
} from '../../services';
-import {UserType} from '../../types/types';
+import {UniversityBadge, UserType} from '../../types/types';
import {getTokenOrLogout} from '../../utils';
import {
clearHeaderAndProfileImages,
+ profileBadgesUpdated,
profileCompletionStageUpdated,
setIsOnboardedUser,
setNewNotificationReceived,
@@ -90,6 +91,25 @@ export const updateSocial = (
}
};
+/**
+ * To update new user badges
+ * @param badges current selection of badges
+ */
+export const updateUserBadges = (
+ badges: UniversityBadge[],
+): ThunkAction<Promise<void>, RootState, unknown, Action<string>> => async (
+ dispatch,
+) => {
+ try {
+ dispatch({
+ type: profileBadgesUpdated.type,
+ payload: {badges},
+ });
+ } catch (error) {
+ console.log(error);
+ }
+};
+
export const updateProfileCompletionStage = (
stage: number,
): ThunkAction<Promise<void>, RootState, unknown, Action<string>> => async (
diff --git a/src/store/initialStates.ts b/src/store/initialStates.ts
index 7fd3ac5a..e0f9d776 100644
--- a/src/store/initialStates.ts
+++ b/src/store/initialStates.ts
@@ -18,6 +18,7 @@ export const NO_PROFILE: ProfileInfoType = {
birthday: undefined,
university_class: 2021,
university: undefined,
+ badges: [],
//Default to an invalid value and ignore it gracefully while showing tutorials / popups.
profile_completion_stage: -1,
suggested_people_linked: -1,
diff --git a/src/store/reducers/userReducer.ts b/src/store/reducers/userReducer.ts
index a8789c1d..97bf845c 100644
--- a/src/store/reducers/userReducer.ts
+++ b/src/store/reducers/userReducer.ts
@@ -42,6 +42,10 @@ const userDataSlice = createSlice({
}
},
+ profileBadgesUpdated: (state, action) => {
+ state.profile.badges = action.payload.badges;
+ },
+
profileCompletionStageUpdated: (state, action) => {
state.profile.profile_completion_stage = action.payload.stage;
},
@@ -90,6 +94,7 @@ export const {
setReplyPosted,
setSuggestedPeopleImage,
clearHeaderAndProfileImages,
+ profileBadgesUpdated,
// setChatClientReady,
} = userDataSlice.actions;
export const userDataReducer = userDataSlice.reducer;
diff --git a/src/types/types.ts b/src/types/types.ts
index 690d6fb9..00501d49 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -49,6 +49,7 @@ export interface ProfileInfoType {
gender: string;
university_class: number;
university: UniversityType;
+ badges: UniversityBadge[];
profile_completion_stage: number;
suggested_people_linked: number;
birthday: Date | undefined;
diff --git a/src/utils/moments.ts b/src/utils/moments.ts
index 87f062af..90d69519 100644
--- a/src/utils/moments.ts
+++ b/src/utils/moments.ts
@@ -37,3 +37,37 @@ export const getTimePosted = (date_time: string) => {
}
return time;
};
+
+export const getTimeInShorthand = (date_time: string) => {
+ const datePosted = moment(date_time);
+ const now = moment();
+ var time = date_time;
+ var difference = now.diff(datePosted, 's');
+
+ // Creating elapsedTime string to display to user
+ // 0 to less than 1 minute
+ if (difference < 60) {
+ time = difference + 's';
+ }
+ // 1 minute to less than 1 hour
+ else if (difference >= 60 && difference < 60 * 60) {
+ difference = now.diff(datePosted, 'm');
+ time = difference + 'm';
+ }
+ // 1 hour to less than 1 day
+ else if (difference >= 60 * 60 && difference < 24 * 60 * 60) {
+ difference = now.diff(datePosted, 'h');
+ time = difference + 'h';
+ }
+ // Any number of days
+ else if (difference >= 24 * 60 * 60 && difference < 24 * 60 * 60 * 7) {
+ difference = now.diff(datePosted, 'd');
+ time = difference + 'd';
+ }
+ // More than 7 days
+ else if (difference >= 24 * 60 * 60 * 7) {
+ difference = now.diff(datePosted, 'w');
+ time = difference + 'w';
+ }
+ return time;
+};
diff --git a/src/utils/users.ts b/src/utils/users.ts
index 754382b3..334cb3c0 100644
--- a/src/utils/users.ts
+++ b/src/utils/users.ts
@@ -1,6 +1,6 @@
import AsyncStorage from '@react-native-community/async-storage';
import {INTEGRATED_SOCIAL_LIST} from '../constants';
-import {isUserBlocked, loadSocialPosts} from '../services';
+import {isUserBlocked, loadSocialPosts, removeBadgesService} from '../services';
import {
loadAllSocials,
loadBlockedList,
@@ -10,6 +10,7 @@ import {
loadUserMoments,
loadUserNotifications,
logout,
+ updateUserBadges,
} from '../store/actions';
import {NO_SOCIAL_ACCOUNTS} from '../store/initialStates';
import {loadUserMomentCategories} from './../store/actions/momentCategories';
@@ -21,6 +22,7 @@ import {
ProfilePreviewType,
ScreenType,
UserType,
+ UniversityBadge,
} from './../types/types';
const loadData = async (dispatch: AppDispatch, user: UserType) => {
@@ -200,6 +202,23 @@ 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,