aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShravya Ramesh <shravs1208@gmail.com>2021-01-15 03:33:59 -0800
committerShravya Ramesh <shravs1208@gmail.com>2021-01-15 03:33:59 -0800
commitdf6595694c678657fec30d881fb1edcd39b62f17 (patch)
tree2953fbbc222fc3f7f092ee61c6d4b0c3414d3f9d
parent82476e27fe6f5dc699370659d223dcd86fd5c76b (diff)
friend request
-rw-r--r--src/components/common/AcceptDeclineButtons.tsx88
-rw-r--r--src/components/common/index.ts1
-rw-r--r--src/components/notifications/Notification.tsx90
-rw-r--r--src/components/profile/Content.tsx67
-rw-r--r--src/components/profile/ProfileBody.tsx100
-rw-r--r--src/routes/Routes.tsx2
-rw-r--r--src/screens/onboarding/WelcomeScreen.tsx7
-rw-r--r--src/services/UserFriendsServices.ts104
-rw-r--r--src/services/UserProfileService.ts5
-rw-r--r--src/store/actions/userFriends.ts102
-rw-r--r--src/store/actions/userX.ts7
-rw-r--r--src/store/initialStates.ts2
-rw-r--r--src/store/reducers/userXReducer.ts7
-rw-r--r--src/types/types.ts6
-rw-r--r--src/utils/users.ts20
15 files changed, 523 insertions, 85 deletions
diff --git a/src/components/common/AcceptDeclineButtons.tsx b/src/components/common/AcceptDeclineButtons.tsx
new file mode 100644
index 00000000..2ebae029
--- /dev/null
+++ b/src/components/common/AcceptDeclineButtons.tsx
@@ -0,0 +1,88 @@
+import React from 'react';
+import {StyleSheet, View} from 'react-native';
+import {Button} from 'react-native-elements';
+import {useDispatch, useStore} from 'react-redux';
+import {
+ declineFriendRequest,
+ loadUserNotifications,
+ updateUserXFriends,
+} from '../../store/actions';
+import {TAGG_TEXT_LIGHT_BLUE} from '../../constants';
+import {acceptFriendRequest} from '../../store/actions';
+import {RootState} from '../../store/rootReducer';
+import {ProfilePreviewType} from '../../types';
+import {SCREEN_WIDTH} from '../../utils';
+
+interface AcceptDeclineButtonsProps {
+ requester: ProfilePreviewType;
+}
+const AcceptDeclineButtons: React.FC<AcceptDeclineButtonsProps> = (props) => {
+ const {requester} = props;
+ const state: RootState = useStore().getState();
+ const dispatch = useDispatch();
+
+ const handleAcceptRequest = async () => {
+ dispatch(acceptFriendRequest(requester));
+ dispatch(updateUserXFriends(requester.id, state));
+ dispatch(loadUserNotifications());
+ };
+
+ const handleDeclineFriendRequest = async () => {
+ dispatch(declineFriendRequest(requester.id));
+ };
+ return (
+ <>
+ <Button
+ title="Accept"
+ buttonStyle={styles.acceptButton}
+ titleStyle={styles.acceptButtonTitle}
+ onPress={() => handleAcceptRequest()}
+ />
+ <Button
+ title="Reject"
+ buttonStyle={styles.rejectButton}
+ titleStyle={styles.rejectButtonTitle}
+ onPress={() => handleDeclineFriendRequest()}
+ />
+ </>
+ );
+};
+
+const styles = StyleSheet.create({
+ acceptButton: {
+ justifyContent: 'center',
+ alignItems: 'center',
+ width: SCREEN_WIDTH * 0.2,
+ height: SCREEN_WIDTH * 0.07,
+ borderRadius: 5,
+ padding: 0,
+ marginRight: '2%',
+ backgroundColor: TAGG_TEXT_LIGHT_BLUE,
+ },
+ rejectButton: {
+ justifyContent: 'center',
+ alignItems: 'center',
+ width: SCREEN_WIDTH * 0.2,
+ height: SCREEN_WIDTH * 0.07,
+ borderColor: TAGG_TEXT_LIGHT_BLUE,
+ borderWidth: 1,
+ borderRadius: 5,
+ marginRight: '2%',
+ padding: 0,
+ backgroundColor: 'white',
+ },
+ rejectButtonTitle: {
+ color: TAGG_TEXT_LIGHT_BLUE,
+ padding: 0,
+ fontSize: 14,
+ fontWeight: '800',
+ },
+ acceptButtonTitle: {
+ color: 'white',
+ padding: 0,
+ fontSize: 14,
+ fontWeight: '800',
+ },
+});
+
+export default AcceptDeclineButtons;
diff --git a/src/components/common/index.ts b/src/components/common/index.ts
index 9162ec70..61c7fa26 100644
--- a/src/components/common/index.ts
+++ b/src/components/common/index.ts
@@ -19,3 +19,4 @@ export {default as TaggLoadingTndicator} from './TaggLoadingIndicator';
export {default as GenericMoreInfoDrawer} from './GenericMoreInfoDrawer';
export {default as TaggPopUp} from './TaggPopup';
export {default as TaggPrompt} from './TaggPrompt';
+export {default as AcceptDeclineButtons} from './AcceptDeclineButtons';
diff --git a/src/components/notifications/Notification.tsx b/src/components/notifications/Notification.tsx
index f6a04526..5e68c6f3 100644
--- a/src/components/notifications/Notification.tsx
+++ b/src/components/notifications/Notification.tsx
@@ -1,17 +1,27 @@
import {useNavigation} from '@react-navigation/native';
import React, {useEffect, useState} from 'react';
import {Image, StyleSheet, Text, View} from 'react-native';
+import {Button} from 'react-native-elements';
import {TouchableWithoutFeedback} from 'react-native-gesture-handler';
import {useDispatch, useStore} from 'react-redux';
-import {loadAvatar} from '../../services';
+import {
+ declineFriendRequest,
+ loadUserNotifications,
+ updateUserXFriends,
+} from '../../store/actions';
+import {TAGG_TEXT_LIGHT_BLUE} from '../../constants';
+import {loadAvatar, unfriendUser} from '../../services';
+import {acceptFriendRequest} from '../../store/actions';
import {RootState} from '../../store/rootReducer';
-import {NotificationType, ScreenType} from '../../types';
+import {NotificationType, ProfilePreviewType, ScreenType} from '../../types';
import {
fetchUserX,
+ getTokenOrLogout,
SCREEN_HEIGHT,
SCREEN_WIDTH,
userXInStore,
} from '../../utils';
+import AcceptDeclineButtons from '../common/AcceptDeclineButtons';
interface NotificationProps {
item: NotificationType;
@@ -29,6 +39,7 @@ const Notification: React.FC<NotificationProps> = (props) => {
},
screenType,
} = props;
+
const navigation = useNavigation();
const state: RootState = useStore().getState();
const dispatch = useDispatch();
@@ -36,7 +47,6 @@ const Notification: React.FC<NotificationProps> = (props) => {
const [avatarURI, setAvatarURI] = useState<string | undefined>(undefined);
const [momentURI, setMomentURI] = useState<string | undefined>(undefined);
const backgroundColor = unread ? '#DCF1F1' : 'rgba(0,0,0,0)';
-
useEffect(() => {
let mounted = true;
const loadAvatarImage = async (user_id: string) => {
@@ -68,7 +78,8 @@ const Notification: React.FC<NotificationProps> = (props) => {
const onNotificationTap = async () => {
switch (notification_type) {
- case 'FRD':
+ case 'FRD_ACPT':
+ case 'FRD_REQ':
if (!userXInStore(state, screenType, id)) {
await fetchUserX(
dispatch,
@@ -86,26 +97,45 @@ const Notification: React.FC<NotificationProps> = (props) => {
}
};
+ // const handleAcceptRequest = async () => {
+ // const requester: ProfilePreviewType = {
+ // id: id,
+ // username: username,
+ // first_name: first_name,
+ // last_name: last_name,
+ // };
+ // dispatch(acceptFriendRequest(requester));
+ // dispatch(updateUserXFriends(id, state));
+ // console.log('fetching notifications since user accepted request!');
+ // dispatch(loadUserNotifications());
+ // };
+
+ // const handleDeclineFriendRequest = async () => {
+ // dispatch(declineFriendRequest(id));
+ // };
+
return (
- <TouchableWithoutFeedback
- style={[styles.container, {backgroundColor}]}
- onPress={onNotificationTap}>
- <View style={styles.avatarContainer}>
- <Image
- style={styles.avatar}
- source={
- avatarURI
- ? {uri: avatarURI, cache: 'only-if-cached'}
- : require('../../assets/images/avatar-placeholder.png')
- }
- />
- </View>
- <View style={styles.contentContainer}>
- <Text style={styles.actorName}>
- {first_name} {last_name}
- </Text>
- <Text>{verbage}</Text>
- </View>
+ <>
+ <TouchableWithoutFeedback
+ style={[styles.container, {backgroundColor}]}
+ onPress={onNotificationTap}>
+ <View style={styles.avatarContainer}>
+ <Image
+ style={styles.avatar}
+ source={
+ avatarURI
+ ? {uri: avatarURI, cache: 'only-if-cached'}
+ : require('../../assets/images/avatar-placeholder.png')
+ }
+ />
+ </View>
+ <View style={styles.contentContainer}>
+ <Text style={styles.actorName}>
+ {first_name} {last_name}
+ </Text>
+ <Text>{verbage}</Text>
+ </View>
+ </TouchableWithoutFeedback>
{/* TODO: Still WIP */}
{/* {notification_type === 'CMT' && notification_object && (
<Image
@@ -113,7 +143,14 @@ const Notification: React.FC<NotificationProps> = (props) => {
source={{uri: momentURI, cache: 'only-if-cached'}}
/>
)} */}
- </TouchableWithoutFeedback>
+ {notification_type === 'FRD_REQ' && (
+ <View style={styles.buttonsContainer}>
+ <AcceptDeclineButtons
+ requester={{id, username, first_name, last_name}}
+ />
+ </View>
+ )}
+ </>
);
};
@@ -123,6 +160,8 @@ const styles = StyleSheet.create({
height: SCREEN_HEIGHT / 10,
flex: 1,
alignItems: 'center',
+ borderColor: 'red',
+ borderWidth: 2,
},
avatarContainer: {
marginLeft: '5%',
@@ -152,6 +191,9 @@ const styles = StyleSheet.create({
width: 42,
right: '5%',
},
+ buttonsContainer: {
+
+ },
});
export default Notification;
diff --git a/src/components/profile/Content.tsx b/src/components/profile/Content.tsx
index 61a08d49..339144d6 100644
--- a/src/components/profile/Content.tsx
+++ b/src/components/profile/Content.tsx
@@ -12,6 +12,8 @@ import {
import Animated from 'react-native-reanimated';
import {
CategorySelectionScreenType,
+ FriendshipStatusType,
+ MomentCategoryType,
MomentType,
ProfilePreviewType,
ProfileType,
@@ -19,7 +21,7 @@ import {
UserType,
} from '../../types';
import {COVER_HEIGHT, TAGG_TEXT_LIGHT_BLUE} from '../../constants';
-import {fetchUserX, moveCategory, SCREEN_HEIGHT, userLogin} from '../../utils';
+import {fetchUserX, getUserAsProfilePreviewType, moveCategory, SCREEN_HEIGHT, userLogin} from '../../utils';
import TaggsBar from '../taggs/TaggsBar';
import {Moment} from '../moments';
import ProfileBody from './ProfileBody';
@@ -188,14 +190,14 @@ const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => {
/**
* This hook is called on load of profile and when you update the friends list.
*/
- useEffect(() => {
- const isActuallyAFriend = friendsLoggedInUser.some(
- (friend) => friend.username === user.username,
- );
- if (isFriend != isActuallyAFriend) {
- setIsFriend(isActuallyAFriend);
- }
- }, [friendsLoggedInUser]);
+ // useEffect(() => {
+ // const isActuallyAFriend = friendsLoggedInUser.some(
+ // (friend) => friend.username === user.username,
+ // );
+ // if (isFriend != isActuallyAFriend) {
+ // setIsFriend(isActuallyAFriend);
+ // }
+ // }, [friendsLoggedInUser]);
useEffect(() => {
const isActuallyBlocked = blockedUsers.some(
@@ -206,22 +208,22 @@ const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => {
}
}, [blockedUsers, user]);
- /**
- * The object returned by this method is added to the list of blocked / friended users by the reducer.
- * Which helps us prevent an extra api call to the backend just to fetch a user.
- */
- const getUserAsProfilePreviewType = (
- passedInUser: UserType,
- passedInProfile: ProfileType,
- ): ProfilePreviewType => {
- const fullName = passedInProfile.name.split(' ');
- return {
- id: passedInUser.userId,
- username: passedInUser.username,
- first_name: fullName[0],
- last_name: fullName[1],
- };
- };
+ // /**
+ // * The object returned by this method is added to the list of blocked / friended users by the reducer.
+ // * Which helps us prevent an extra api call to the backend just to fetch a user.
+ // */
+ // const getUserAsProfilePreviewType = (
+ // passedInUser: UserType,
+ // passedInProfile: ProfileType,
+ // ): ProfilePreviewType => {
+ // const fullName = passedInProfile.name.split(' ');
+ // return {
+ // id: passedInUser.userId,
+ // username: passedInUser.username,
+ // first_name: fullName[0],
+ // last_name: fullName[1],
+ // };
+ // };
/**
* Handles a click on the friend / unfriend button.
@@ -229,12 +231,25 @@ const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => {
* updateUserXFriends updates friends list for the new friend.
*/
+ // Handles click on friend/requested/unfriend button
+ /*
+ * When user logged in clicks on the friend button:
+ A request is sent.
+ Which means you have to update the status of their friendshpi to requested
+ When the status is changed to requested the button should change to requested.
+ When the button is changed to requested and thr user clicks on it,
+ a request much go to the backend to delete that request
+ When that succeeds, their friendship must be updated to no-record again;
+ When the button is changed to no_record, the add friends button should be displayed again
+ */
const handleFriendUnfriend = async () => {
+ const {friendship_status} = profile;
await dispatch(
friendUnfriendUser(
loggedInUser,
getUserAsProfilePreviewType(user, profile),
- isFriend,
+ friendship_status,
+ screenType,
),
);
await dispatch(updateUserXFriends(user.userId, state));
diff --git a/src/components/profile/ProfileBody.tsx b/src/components/profile/ProfileBody.tsx
index 57b617d8..87c12424 100644
--- a/src/components/profile/ProfileBody.tsx
+++ b/src/components/profile/ProfileBody.tsx
@@ -1,15 +1,21 @@
import React from 'react';
import {StyleSheet, View, Text, LayoutChangeEvent, Linking} from 'react-native';
-import {TAGG_DARK_BLUE, TOGGLE_BUTTON_TYPE} from '../../constants';
+import {Button} from 'react-native-elements';
+import {
+ TAGG_DARK_BLUE,
+ TAGG_TEXT_LIGHT_BLUE,
+ TOGGLE_BUTTON_TYPE,
+} from '../../constants';
import ToggleButton from './ToggleButton';
import {RootState} from '../../store/rootReducer';
import {useSelector} from 'react-redux';
-import {ScreenType} from '../../types';
+import {FriendshipStatusType, ScreenType} from '../../types';
import {NO_PROFILE} from '../../store/initialStates';
+import {getUserAsProfilePreviewType, SCREEN_WIDTH} from '../../utils';
+import {AcceptDeclineButtons} from '../common';
interface ProfileBodyProps {
onLayout: (event: LayoutChangeEvent) => void;
- isFriend: boolean;
isBlocked: boolean;
handleFriendUnfriend: () => void;
handleBlockUnblock: () => void;
@@ -18,7 +24,6 @@ interface ProfileBodyProps {
}
const ProfileBody: React.FC<ProfileBodyProps> = ({
onLayout,
- isFriend,
isBlocked,
handleFriendUnfriend,
handleBlockUnblock,
@@ -32,7 +37,13 @@ const ProfileBody: React.FC<ProfileBodyProps> = ({
? useSelector((state: RootState) => state.userX[screenType][userXId])
: useSelector((state: RootState) => state.user);
- const {biography, website} = profile;
+ const {
+ biography,
+ website,
+ friendship_status,
+ friendship_requester_id,
+ } = profile;
+
return (
<View onLayout={onLayout} style={styles.container}>
<Text style={styles.username}>{`@${username}`}</Text>
@@ -57,24 +68,51 @@ const ProfileBody: React.FC<ProfileBodyProps> = ({
buttonType={TOGGLE_BUTTON_TYPE.BLOCK_UNBLOCK}
/>
</View>
-
)}
{userXId && !isBlocked && (
- <View style={styles.toggleButtonContainer}>
- <ToggleButton
- toggleState={isFriend}
- handleToggle={handleFriendUnfriend}
- buttonType={TOGGLE_BUTTON_TYPE.FRIEND_UNFRIEND}
- />
+ <View style={styles.buttonsContainer}>
+ {friendship_status === 'no_record' && (
+ <Button
+ title={'Add Friend'}
+ buttonStyle={styles.button}
+ titleStyle={styles.buttonTitle}
+ onPress={handleFriendUnfriend} // requested, requested status
+ />
+ )}
+ {friendship_status === 'friends' && (
+ <Button
+ title={'Unfriend'}
+ buttonStyle={styles.button}
+ titleStyle={styles.buttonTitle}
+ onPress={handleFriendUnfriend} // unfriend, no record status
+ />
+ )}
+ {(friendship_status === 'requested' &&
+ friendship_requester_id !== userXId && (
+ <Button
+ title={'Requested'}
+ buttonStyle={styles.requestedButton}
+ titleStyle={styles.requestedButtonTitle}
+ onPress={handleFriendUnfriend} // delete request, no record status
+ />
+ )) ||
+ (friendship_status === 'requested' &&
+ friendship_requester_id === userXId && (
+ <AcceptDeclineButtons
+ requester={getUserAsProfilePreviewType(
+ {userId: userXId, username},
+ profile,
+ )}
+ />
+ ))}
</View>
-
)}
</View>
);
};
const styles = StyleSheet.create({
- toggleButtonContainer: {
+ buttonsContainer: {
flexDirection: 'row',
flex: 1,
paddingTop: '3.5%',
@@ -99,6 +137,40 @@ const styles = StyleSheet.create({
color: TAGG_DARK_BLUE,
marginBottom: '1%',
},
+ requestedButton: {
+ justifyContent: 'center',
+ alignItems: 'center',
+ width: SCREEN_WIDTH * 0.4,
+ height: SCREEN_WIDTH * 0.09,
+ borderColor: TAGG_TEXT_LIGHT_BLUE,
+ borderWidth: 3,
+ borderRadius: 5,
+ marginRight: '2%',
+ padding: 0,
+ backgroundColor: 'transparent',
+ },
+ requestedButtonTitle: {
+ color: TAGG_TEXT_LIGHT_BLUE,
+ padding: 0,
+ fontSize: 14,
+ fontWeight: '700',
+ },
+ buttonTitle: {
+ color: 'white',
+ padding: 0,
+ fontSize: 14,
+ fontWeight: '700',
+ },
+ button: {
+ justifyContent: 'center',
+ alignItems: 'center',
+ width: SCREEN_WIDTH * 0.4,
+ height: SCREEN_WIDTH * 0.09,
+ padding: 0,
+ borderRadius: 5,
+ marginRight: '2%',
+ backgroundColor: TAGG_TEXT_LIGHT_BLUE,
+ },
});
export default ProfileBody;
diff --git a/src/routes/Routes.tsx b/src/routes/Routes.tsx
index 38a987f7..c64abcfc 100644
--- a/src/routes/Routes.tsx
+++ b/src/routes/Routes.tsx
@@ -26,7 +26,7 @@ const Routes: React.FC = () => {
*/
useEffect(() => {
if (!userId) {
- userLogin(dispatch, {userId: '', username: ''});
+ // userLogin(dispatch, {userId: '', username: ''});
} else {
SplashScreen.hide();
}
diff --git a/src/screens/onboarding/WelcomeScreen.tsx b/src/screens/onboarding/WelcomeScreen.tsx
index 96d3f929..c84f03a9 100644
--- a/src/screens/onboarding/WelcomeScreen.tsx
+++ b/src/screens/onboarding/WelcomeScreen.tsx
@@ -17,7 +17,12 @@ interface WelcomeScreenProps {
const WelcomeScreen: React.FC<WelcomeScreenProps> = ({navigation}) => {
const handleNext = () => {
- navigation.navigate('InvitationCodeVerification');
+ navigation.navigate('RegistrationThree', {
+ lastName: 'one',
+ firstName: 'chunty',
+ email: 'chunty1@gmail.com',
+ phone: '6263495836',
+ });
};
return (
<Background
diff --git a/src/services/UserFriendsServices.ts b/src/services/UserFriendsServices.ts
index 0b138fc3..9235d890 100644
--- a/src/services/UserFriendsServices.ts
+++ b/src/services/UserFriendsServices.ts
@@ -1,6 +1,7 @@
//Abstracted common friends api calls out here
import {Alert} from 'react-native';
+import {FriendshipStatusType} from 'src/types';
import {FRIENDS_ENDPOINT} from '../constants';
export const loadFriends = async (userId: string, token: string) => {
@@ -26,19 +27,77 @@ export const friendOrUnfriendUser = async (
user: string,
friend: string,
token: string,
- isFriend: boolean,
+ friendship_status: FriendshipStatusType,
) => {
try {
- const endpoint = FRIENDS_ENDPOINT + (isFriend ? `${user}/` : '');
+ let body;
+ let method = '';
+ let endpoint = FRIENDS_ENDPOINT;
+
+ switch (friendship_status) {
+ case 'no_record':
+ method = 'POST';
+ body = JSON.stringify({
+ requested: friend,
+ });
+ break;
+ case 'requested':
+ method = 'DELETE';
+ endpoint += `${friend}/`;
+ body = JSON.stringify({
+ reason: 'cancelled',
+ });
+ break;
+ case 'friends':
+ method = 'DELETE';
+ endpoint += `${friend}/`;
+ body = JSON.stringify({
+ reason: 'unfriended',
+ });
+ }
+
const response = await fetch(endpoint, {
- method: isFriend ? 'DELETE' : 'POST',
+ method: method,
headers: {
'Content-Type': 'application/json',
Authorization: 'Token ' + token,
},
+ body: body,
+ });
+
+ const status = response.status;
+ if (Math.floor(status / 100) === 2) {
+ return true;
+ } else {
+ console.log(await response.json());
+ Alert.alert(
+ 'Something went wrong! 😭',
+ "Would you believe me if I told you that I don't know what happened?",
+ );
+ return false;
+ }
+ } catch (error) {
+ console.log(error);
+ Alert.alert(
+ 'Something went wrong! 😭',
+ "Would you believe me if I told you that I don't know what happened?",
+ );
+ return false;
+ }
+};
+
+export const declineFriendRequestService = async (
+ user_id: string,
+ token: string | null,
+) => {
+ try {
+ const response = await fetch(FRIENDS_ENDPOINT + `${user_id}/`, {
+ method: 'DELETE',
+ headers: {
+ Authorization: 'Token ' + token,
+ },
body: JSON.stringify({
- user,
- friend,
+ reason: 'declined',
}),
});
const status = response.status;
@@ -61,3 +120,38 @@ export const friendOrUnfriendUser = async (
return false;
}
};
+
+export const acceptFriendRequestService = async (
+ requester_id: string,
+ token: string | null,
+) => {
+ try {
+ console.log('requester_id: ', requester_id);
+ console.log('token: ', token);
+ const response = await fetch(FRIENDS_ENDPOINT + `${requester_id}/`, {
+ method: 'PATCH',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: 'Token ' + token,
+ },
+ });
+ const status = response.status;
+ if (Math.floor(status / 100) === 2) {
+ return true;
+ } else {
+ console.log(await response.json());
+ Alert.alert(
+ 'Something went wrong! 😭',
+ "Would you believe me if I told you that I don't know what happened?",
+ );
+ return false;
+ }
+ } catch (error) {
+ console.log(error);
+ Alert.alert(
+ 'Something went wrong! 😭',
+ "Would you believe me if I told you that I don't know what happened?",
+ );
+ return false;
+ }
+};
diff --git a/src/services/UserProfileService.ts b/src/services/UserProfileService.ts
index 793ee44d..80ab4fff 100644
--- a/src/services/UserProfileService.ts
+++ b/src/services/UserProfileService.ts
@@ -39,7 +39,10 @@ export const loadProfileInfo = async (token: string, userId: string) => {
tiktok,
university_class,
profile_completion_stage,
+ friendship_status,
+ friendship_requester_id,
} = info;
+ console.log('friendship_requester: ', friendship_requester_id);
birthday = birthday && moment(birthday).format('YYYY-MM-DD');
return {
name,
@@ -51,6 +54,8 @@ export const loadProfileInfo = async (token: string, userId: string) => {
tiktok,
university_class,
profile_completion_stage,
+ friendship_status,
+ friendship_requester_id,
};
} else {
throw 'Unable to load profile data';
diff --git a/src/store/actions/userFriends.ts b/src/store/actions/userFriends.ts
index 24e32607..010dc5ed 100644
--- a/src/store/actions/userFriends.ts
+++ b/src/store/actions/userFriends.ts
@@ -1,9 +1,24 @@
import {getTokenOrLogout} from '../../utils';
import {RootState} from '../rootReducer';
-import {ProfilePreviewType, UserType} from '../../types/types';
-import {friendOrUnfriendUser, loadFriends} from '../../services';
+import {
+ FriendshipStatusType,
+ ProfilePreviewType,
+ ScreenType,
+ UserType,
+} from '../../types/types';
+import {
+ acceptFriendRequestService,
+ declineFriendRequestService,
+ friendOrUnfriendUser,
+ loadFriends,
+} from '../../services';
import {Action, ThunkAction} from '@reduxjs/toolkit';
-import {userFriendsFetched, updateFriends} from '../reducers';
+import {
+ userFriendsFetched,
+ updateFriends,
+ userXFriendshipEdited,
+ userLoggedIn,
+} from '../reducers';
export const loadFriendsData = (
userId: string,
@@ -23,26 +38,69 @@ export const loadFriendsData = (
};
export const friendUnfriendUser = (
- user: UserType,
- friend: ProfilePreviewType,
- isFriend: boolean,
+ user: UserType, //logged in user
+ friend: ProfilePreviewType, // userX's profile preview
+ friendship_status: FriendshipStatusType, // friendshp status with userx
+ screenType: ScreenType, //screentype from content
): ThunkAction<Promise<void>, RootState, unknown, Action<string>> => async (
dispatch,
) => {
try {
const token = await getTokenOrLogout(dispatch);
+ // Calls method to send post or delete request
const success = await friendOrUnfriendUser(
user.userId,
friend.id,
token,
- isFriend,
+ friendship_status,
);
if (success) {
+ let data = 'no_record';
+ switch (friendship_status) {
+ case 'no_record': // send request: update to requested
+ data = 'requested';
+ break;
+ case 'requested': // cancel request: update to no relationship
+ case 'friends': // unfriend: update to no relationship
+ dispatch({
+ type: updateFriends.type,
+ payload: {
+ friend,
+ isFriend: true,
+ },
+ });
+ data = 'no_record';
+ }
+ // Update loggedInUser's friends list
+ console.log('friendship_status data: ', data);
+ dispatch({
+ type: userXFriendshipEdited.type,
+ payload: {
+ userId: friend.id,
+ screenType,
+ data,
+ },
+ });
+ }
+ } catch (error) {
+ console.log(error);
+ }
+};
+
+export const acceptFriendRequest = (
+ requester: ProfilePreviewType,
+): ThunkAction<Promise<void>, RootState, unknown, Action<string>> => async (
+ dispatch,
+) => {
+ try {
+ const token = await getTokenOrLogout(dispatch);
+ const success = await acceptFriendRequestService(requester.id, token);
+ if (success) {
dispatch({
type: updateFriends.type,
payload: {
- isFriend,
- data: friend,
+ data: requester,
+ isFriend: false,
},
});
}
@@ -50,3 +108,29 @@ export const friendUnfriendUser = (
console.log(error);
}
};
+
+export const declineFriendRequest = (
+ user_id: string,
+): ThunkAction<Promise<void>, RootState, unknown, Action<string>> => async (
+ dispatch,
+) => {
+ try {
+ console.log('Requesting service to reject friend request');
+ const token = await getTokenOrLogout(dispatch);
+ const success = await declineFriendRequestService(user_id, token);
+ if (success) {
+ // Get profile of requester
+ console.log('declined request: ', success);
+ // dispatch({
+ // type: updateFriends.type,
+ // payload: {
+ // data: requester, // has to be a requester not id
+ // },
+ // });
+ } else {
+ console.log('Unsuccessful call');
+ }
+ } catch (error) {
+ console.log(error);
+ }
+};
diff --git a/src/store/actions/userX.ts b/src/store/actions/userX.ts
index 0f87012d..2f910052 100644
--- a/src/store/actions/userX.ts
+++ b/src/store/actions/userX.ts
@@ -38,12 +38,13 @@ export const loadUserX = (
payload: {screenType, userId, user},
});
const token = await getTokenOrLogout(dispatch);
- loadProfileInfo(token, userId).then((data) =>
+ loadProfileInfo(token, userId).then((data) => {
+ console.log('DATA FETCHED: ', data);
dispatch({
type: userXProfileFetched.type,
payload: {screenType, userId, data},
- }),
- );
+ });
+ });
loadAllSocialsForUser(userId).then((data) =>
dispatch({
type: userXSocialsFetched.type,
diff --git a/src/store/initialStates.ts b/src/store/initialStates.ts
index 87e1ce22..08dc7077 100644
--- a/src/store/initialStates.ts
+++ b/src/store/initialStates.ts
@@ -20,6 +20,8 @@ export const NO_PROFILE: ProfileType = {
profile_completion_stage: 1,
snapchat: '',
tiktok: '',
+ friendship_status: 'no_record',
+ friendship_requester_id: '',
};
export const EMPTY_MOMENTS_LIST = <MomentType[]>[];
diff --git a/src/store/reducers/userXReducer.ts b/src/store/reducers/userXReducer.ts
index 3b00cf88..9f90d58d 100644
--- a/src/store/reducers/userXReducer.ts
+++ b/src/store/reducers/userXReducer.ts
@@ -60,6 +60,12 @@ const userXSlice = createSlice({
].socialAccounts = action.payload.data;
},
+ userXFriendshipEdited: (state, action) => {
+ state[<ScreenType>action.payload.screenType][
+ action.payload.userId
+ ].profile.friendship_status = action.payload.data;
+ },
+
resetScreen: (state, action) => {
for (let userId in state[<ScreenType>action.payload.screenType]) {
state[<ScreenType>action.payload.screenType][userId] = EMPTY_USER_X;
@@ -78,6 +84,7 @@ export const {
userXProfileFetched,
userXSocialsFetched,
userXMomentCategoriesFetched,
+ userXFriendshipEdited,
resetScreen,
} = userXSlice.actions;
export const userXReducer = userXSlice.reducer;
diff --git a/src/types/types.ts b/src/types/types.ts
index 093adbe4..d9d0b56b 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -13,6 +13,8 @@ export interface ProfilePreviewType {
last_name: string;
}
+export type FriendshipStatusType = 'friends' | 'requested' | 'no_record';
+
export interface ProfileType {
name: string;
biography: string;
@@ -23,6 +25,8 @@ export interface ProfileType {
birthday: Date | undefined;
snapchat: string;
tiktok: string;
+ friendship_status: FriendshipStatusType;
+ friendship_requester_id: string;
}
export interface SocialAccountType {
@@ -165,7 +169,7 @@ export type TaggPopupType = {
export type NotificationType = {
actor: ProfilePreviewType;
verbage: string;
- notification_type: 'DFT' | 'FRD' | 'CMT';
+ notification_type: 'DFT' | 'FRD_REQ' | 'FRD_ACPT' | 'FRD_DEC' | 'CMT';
notification_object: CommentType | undefined;
timestamp: string;
unread: boolean;
diff --git a/src/utils/users.ts b/src/utils/users.ts
index c54ea715..2a7db214 100644
--- a/src/utils/users.ts
+++ b/src/utils/users.ts
@@ -16,7 +16,7 @@ import {loadUserMomentCategories} from './../store/actions/momentCategories';
import {loadUserX} from './../store/actions/userX';
import {AppDispatch} from './../store/configureStore';
import {RootState} from './../store/rootReducer';
-import {ScreenType, UserType} from './../types/types';
+import {ProfilePreviewType, ProfileType, ScreenType, UserType} from './../types/types';
const loadData = async (dispatch: AppDispatch, user: UserType) => {
await Promise.all([
@@ -122,3 +122,21 @@ export const getTokenOrLogout = async (dispatch: Function): Promise<string> => {
}
return token;
};
+
+/**
+ * Creates ProfilePreviewType of a user using UserType && ProfileType
+ * @param passedInUser This is the UserType of the user
+ * @param passedInProfile This is the ProfileType of the user
+ */
+export const getUserAsProfilePreviewType = (
+ passedInUser: UserType,
+ passedInProfile: ProfileType,
+): ProfilePreviewType => {
+ const fullName = passedInProfile.name.split(' ');
+ return {
+ id: passedInUser.userId,
+ username: passedInUser.username,
+ first_name: fullName[0],
+ last_name: fullName[1],
+ };
+};