diff options
-rw-r--r-- | src/components/common/AcceptDeclineButtons.tsx | 88 | ||||
-rw-r--r-- | src/components/common/index.ts | 1 | ||||
-rw-r--r-- | src/components/notifications/Notification.tsx | 90 | ||||
-rw-r--r-- | src/components/profile/Content.tsx | 67 | ||||
-rw-r--r-- | src/components/profile/ProfileBody.tsx | 100 | ||||
-rw-r--r-- | src/routes/Routes.tsx | 2 | ||||
-rw-r--r-- | src/screens/onboarding/WelcomeScreen.tsx | 7 | ||||
-rw-r--r-- | src/services/UserFriendsServices.ts | 104 | ||||
-rw-r--r-- | src/services/UserProfileService.ts | 5 | ||||
-rw-r--r-- | src/store/actions/userFriends.ts | 102 | ||||
-rw-r--r-- | src/store/actions/userX.ts | 7 | ||||
-rw-r--r-- | src/store/initialStates.ts | 2 | ||||
-rw-r--r-- | src/store/reducers/userXReducer.ts | 7 | ||||
-rw-r--r-- | src/types/types.ts | 6 | ||||
-rw-r--r-- | src/utils/users.ts | 20 |
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], + }; +}; |