aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/assets/images/images.pngbin0 -> 20323 bytes
-rw-r--r--src/assets/images/suggested-people-preview-large.pngbin0 -> 1246502 bytes
-rw-r--r--src/assets/images/suggested-people-preview-silhouette.pngbin0 -> 6899 bytes
-rw-r--r--src/assets/images/suggested-people-preview-small.pngbin0 -> 1110201 bytes
-rw-r--r--src/assets/images/tagg-logo.pngbin0 -> 34714 bytes
-rw-r--r--src/components/common/FriendsButton.tsx148
-rw-r--r--src/components/profile/ProfileBody.tsx100
-rw-r--r--src/constants/api.ts4
-rw-r--r--src/constants/constants.ts3
-rw-r--r--src/constants/strings.ts1
-rw-r--r--src/routes/suggestedPeopleOnboarding/SuggestedPeopleOnboardingStackNavigator.tsx8
-rw-r--r--src/routes/suggestedPeopleOnboarding/SuggestedPeopleOnboardingStackScreen.tsx36
-rw-r--r--src/routes/suggestedPeopleOnboarding/index.ts2
-rw-r--r--src/screens/index.ts1
-rw-r--r--src/screens/suggestedPeople/AnimatedTutorial.tsx16
-rw-r--r--src/screens/suggestedPeople/SuggestedPeopleScreen.tsx110
-rw-r--r--src/screens/suggestedPeopleOnboarding/SuggestedPeopleUploadPictureScreen.tsx164
-rw-r--r--src/screens/suggestedPeopleOnboarding/SuggestedPeopleWelcomeScreen.tsx81
-rw-r--r--src/screens/suggestedPeopleOnboarding/index.ts2
-rw-r--r--src/services/SuggestedPeopleService.ts50
-rw-r--r--src/services/UserProfileService.ts45
-rw-r--r--src/services/index.ts1
-rw-r--r--src/store/actions/user.ts34
-rw-r--r--src/store/initialStates.ts2
-rw-r--r--src/store/reducers/userReducer.ts6
-rw-r--r--src/types/types.ts2
26 files changed, 551 insertions, 265 deletions
diff --git a/src/assets/images/images.png b/src/assets/images/images.png
new file mode 100644
index 00000000..43f42556
--- /dev/null
+++ b/src/assets/images/images.png
Binary files differ
diff --git a/src/assets/images/suggested-people-preview-large.png b/src/assets/images/suggested-people-preview-large.png
new file mode 100644
index 00000000..b80eda27
--- /dev/null
+++ b/src/assets/images/suggested-people-preview-large.png
Binary files differ
diff --git a/src/assets/images/suggested-people-preview-silhouette.png b/src/assets/images/suggested-people-preview-silhouette.png
new file mode 100644
index 00000000..7c496013
--- /dev/null
+++ b/src/assets/images/suggested-people-preview-silhouette.png
Binary files differ
diff --git a/src/assets/images/suggested-people-preview-small.png b/src/assets/images/suggested-people-preview-small.png
new file mode 100644
index 00000000..bb1aef89
--- /dev/null
+++ b/src/assets/images/suggested-people-preview-small.png
Binary files differ
diff --git a/src/assets/images/tagg-logo.png b/src/assets/images/tagg-logo.png
new file mode 100644
index 00000000..8d67f1f7
--- /dev/null
+++ b/src/assets/images/tagg-logo.png
Binary files differ
diff --git a/src/components/common/FriendsButton.tsx b/src/components/common/FriendsButton.tsx
index 243a551d..a1e107c5 100644
--- a/src/components/common/FriendsButton.tsx
+++ b/src/components/common/FriendsButton.tsx
@@ -1,18 +1,34 @@
-import React from 'react';
-import {StyleSheet} from 'react-native';
-import {Button} from 'react-native-elements';
-import {ScreenType} from '../../types';
+import React, {Fragment} from 'react';
+import {
+ StyleProp,
+ StyleSheet,
+ Text,
+ TextStyle,
+ View,
+ ViewStyle,
+} from 'react-native';
+import {TouchableOpacity} from 'react-native-gesture-handler';
+import {useDispatch, useSelector, useStore} from 'react-redux';
import {TAGG_LIGHT_BLUE} from '../../constants';
-import {handleFriendUnfriend, SCREEN_WIDTH} from '../../utils';
import {NO_PROFILE, NO_USER} from '../../store/initialStates';
-import {useDispatch, useSelector, useStore} from 'react-redux';
import {RootState} from '../../store/rootReducer';
+import {ScreenType} from '../../types';
+import {handleFriendUnfriend, normalize, SCREEN_WIDTH} from '../../utils';
-interface ProfileBodyProps {
+interface FriendsButtonProps {
userXId: string | undefined;
screenType: ScreenType;
+ friendship_requester_id: string;
+ onAcceptRequest: () => void;
+ onRejectRequest: () => void;
}
-const FriendsButton: React.FC<ProfileBodyProps> = ({userXId, screenType}) => {
+const FriendsButton: React.FC<FriendsButtonProps> = ({
+ userXId,
+ screenType,
+ friendship_requester_id,
+ onAcceptRequest,
+ onRejectRequest,
+}) => {
const dispatch = useDispatch();
const {user = NO_USER, profile = NO_PROFILE} = userXId
@@ -24,35 +40,29 @@ const FriendsButton: React.FC<ProfileBodyProps> = ({userXId, screenType}) => {
);
const state = useStore().getState();
-
const {friendship_status} = profile;
- return (
- <>
- {friendship_status === 'no_record' && (
- <Button
- title={'Add Friend'}
- buttonStyle={styles.button}
- titleStyle={styles.buttonTitle}
- onPress={() =>
- handleFriendUnfriend(
- screenType,
- user,
- profile,
- dispatch,
- state,
- loggedInUser,
- )
- } // requested, requested status
- />
- )}
- {friendship_status === 'friends' && (
- <Button
- title={'Unfriend'}
- buttonStyle={styles.requestedButton}
- titleStyle={styles.requestedButtonTitle}
- onPress={() =>
- handleFriendUnfriend(
+ const outlineButton: [StyleProp<ViewStyle>, StyleProp<TextStyle>] = [
+ [styles.button, styles.transparentBG],
+ [styles.label, styles.blueLabel],
+ ];
+
+ const filledButton: [StyleProp<ViewStyle>, StyleProp<TextStyle>] = [
+ [styles.button, styles.lightBlueBG],
+ [styles.label, styles.whiteLabel],
+ ];
+
+ const renderButton = (
+ title: string,
+ style: [StyleProp<ViewStyle>, StyleProp<TextStyle>],
+ onPress?: () => void,
+ ) => (
+ <TouchableOpacity
+ style={style[0]}
+ onPress={() =>
+ onPress
+ ? onPress()
+ : handleFriendUnfriend(
screenType,
user,
profile,
@@ -60,50 +70,64 @@ const FriendsButton: React.FC<ProfileBodyProps> = ({userXId, screenType}) => {
state,
loggedInUser,
)
- } // unfriend, no record status
- />
- )}
- </>
+ }>
+ <Text style={style[1]}>{title}</Text>
+ </TouchableOpacity>
);
+
+ switch (friendship_status) {
+ case 'friends':
+ return renderButton('Unfriend', outlineButton);
+ case 'requested':
+ if (friendship_requester_id !== userXId) {
+ return renderButton('Requested', outlineButton);
+ } else {
+ return (
+ <View style={styles.row}>
+ {renderButton('Accept', filledButton, onAcceptRequest)}
+ {renderButton('Reject', outlineButton, onRejectRequest)}
+ </View>
+ );
+ }
+ case 'no_record':
+ return renderButton('Add Friend', filledButton);
+ default:
+ return <Fragment />;
+ }
};
const styles = StyleSheet.create({
- requestedButton: {
+ button: {
justifyContent: 'center',
alignItems: 'center',
width: SCREEN_WIDTH * 0.4,
- height: SCREEN_WIDTH * 0.075,
- borderColor: TAGG_LIGHT_BLUE,
+ aspectRatio: 154 / 33,
borderWidth: 2,
+ borderColor: TAGG_LIGHT_BLUE,
borderRadius: 3,
marginRight: '2%',
marginLeft: '1%',
+ },
+ transparentBG: {
backgroundColor: 'transparent',
},
- requestedButtonTitle: {
- color: TAGG_LIGHT_BLUE,
- padding: 0,
- fontSize: 14,
+ lightBlueBG: {
+ backgroundColor: TAGG_LIGHT_BLUE,
+ },
+ label: {
+ fontSize: normalize(15),
fontWeight: '700',
+ letterSpacing: 1,
},
- buttonTitle: {
+ blueLabel: {
+ color: TAGG_LIGHT_BLUE,
+ },
+ whiteLabel: {
color: 'white',
- padding: 0,
- fontSize: 14,
- fontWeight: '700',
},
- button: {
- justifyContent: 'center',
- alignItems: 'center',
- width: SCREEN_WIDTH * 0.4,
- height: SCREEN_WIDTH * 0.075,
- padding: 0,
- borderWidth: 2,
- borderColor: TAGG_LIGHT_BLUE,
- borderRadius: 3,
- marginRight: '2%',
- marginLeft: '1%',
- backgroundColor: TAGG_LIGHT_BLUE,
+ row: {
+ flex: 1,
+ flexDirection: 'row',
},
});
diff --git a/src/components/profile/ProfileBody.tsx b/src/components/profile/ProfileBody.tsx
index 106b20a7..646be3e0 100644
--- a/src/components/profile/ProfileBody.tsx
+++ b/src/components/profile/ProfileBody.tsx
@@ -1,28 +1,24 @@
import React from 'react';
-import {StyleSheet, View, Text, LayoutChangeEvent, Linking} from 'react-native';
-import {Button, normalize} from 'react-native-elements';
+import {LayoutChangeEvent, Linking, StyleSheet, Text, View} from 'react-native';
+import {normalize} from 'react-native-elements';
+import {useDispatch, useSelector, useStore} from 'react-redux';
import {
TAGG_DARK_BLUE,
TAGG_LIGHT_BLUE,
TOGGLE_BUTTON_TYPE,
} from '../../constants';
-import ToggleButton from './ToggleButton';
-import {RootState} from '../../store/rootReducer';
-import {useDispatch, useSelector, useStore} from 'react-redux';
-import {ScreenType} from '../../types';
-import {NO_PROFILE, NO_USER} from '../../store/initialStates';
-import {
- getUserAsProfilePreviewType,
- handleFriendUnfriend,
- SCREEN_WIDTH,
-} from '../../utils';
-import {AcceptDeclineButtons, FriendsButton} from '../common';
import {
acceptFriendRequest,
declineFriendRequest,
updateUserXFriends,
updateUserXProfileAllScreens,
} from '../../store/actions';
+import {NO_PROFILE, NO_USER} from '../../store/initialStates';
+import {RootState} from '../../store/rootReducer';
+import {ScreenType} from '../../types';
+import {getUserAsProfilePreviewType, SCREEN_WIDTH} from '../../utils';
+import {FriendsButton} from '../common';
+import ToggleButton from './ToggleButton';
interface ProfileBodyProps {
onLayout: (event: LayoutChangeEvent) => void;
@@ -99,37 +95,13 @@ const ProfileBody: React.FC<ProfileBodyProps> = ({
)}
{userXId && !isBlocked && (
<View style={styles.buttonsContainer}>
- <FriendsButton userXId={userXId} screenType={screenType} />
- {(friendship_status === 'requested' &&
- friendship_requester_id !== userXId && (
- <Button
- title={'Requested'}
- buttonStyle={styles.requestedButton}
- titleStyle={styles.requestedButtonTitle}
- onPress={() =>
- handleFriendUnfriend(
- screenType,
- user,
- profile,
- dispatch,
- state,
- loggedInUser,
- )
- } // delete request, no record status
- />
- )) ||
- (friendship_status === 'requested' &&
- friendship_requester_id === userXId && (
- <AcceptDeclineButtons
- requester={getUserAsProfilePreviewType(
- {userId: userXId, username},
- profile,
- )}
- onAccept={handleAcceptRequest}
- onReject={handleDeclineFriendRequest}
- externalStyles={{container: styles.acceptRejectContainer}}
- />
- ))}
+ <FriendsButton
+ userXId={userXId}
+ screenType={screenType}
+ friendship_requester_id={friendship_requester_id}
+ onAcceptRequest={handleAcceptRequest}
+ onRejectRequest={handleDeclineFriendRequest}
+ />
</View>
)}
</View>
@@ -143,11 +115,7 @@ const styles = StyleSheet.create({
paddingTop: '3.5%',
paddingBottom: '2%',
},
- acceptRejectContainer: {
- flexDirection: 'row',
- },
buttonsContainer: {
- flexDirection: 'row',
flex: 1,
paddingTop: '3.5%',
paddingBottom: '2%',
@@ -171,42 +139,6 @@ const styles = StyleSheet.create({
color: TAGG_DARK_BLUE,
marginBottom: '1%',
},
- requestedButton: {
- justifyContent: 'center',
- alignItems: 'center',
- width: SCREEN_WIDTH * 0.4,
- height: SCREEN_WIDTH * 0.075,
- borderColor: TAGG_LIGHT_BLUE,
- borderWidth: 2,
- borderRadius: 3,
- marginRight: '2%',
- padding: 0,
- backgroundColor: 'transparent',
- },
- requestedButtonTitle: {
- color: TAGG_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.075,
- padding: 0,
- borderWidth: 2,
- borderColor: TAGG_LIGHT_BLUE,
- borderRadius: 3,
- marginRight: '2%',
- backgroundColor: TAGG_LIGHT_BLUE,
- },
});
export default ProfileBody;
diff --git a/src/constants/api.ts b/src/constants/api.ts
index 165bd550..215dadc0 100644
--- a/src/constants/api.ts
+++ b/src/constants/api.ts
@@ -1,3 +1,4 @@
+/* eslint-disable */
// const BASE_URL: string = 'http://3.22.188.127/'; // prod server
const BASE_URL: string = 'http://127.0.0.1:8000/'; // local server
@@ -32,6 +33,9 @@ export const DISCOVER_ENDPOINT: string = API_URL + 'discover/';
export const WAITLIST_USER_ENDPOINT: string = API_URL + 'waitlist-user/';
export const COMMENT_THREAD_ENDPOINT: string = API_URL + 'reply/';
+// Suggested People
+export const SP_UPDATE_PICTURE: string = API_URL + 'suggested_people/update_picture/';
+
// Register as FCM device
export const FCM_ENDPOINT: string = API_URL + 'fcm/';
diff --git a/src/constants/constants.ts b/src/constants/constants.ts
index 3c43fb6c..fbf03744 100644
--- a/src/constants/constants.ts
+++ b/src/constants/constants.ts
@@ -181,3 +181,6 @@ export const EXPLORE_SECTION_TITLES: ExploreSectionType[] = [
"Brown '22",
"Brown '23",
];
+
+export const SP_WIDTH = 375;
+export const SP_HEIGHT = 812;
diff --git a/src/constants/strings.ts b/src/constants/strings.ts
index 5a9be5fc..0965bad0 100644
--- a/src/constants/strings.ts
+++ b/src/constants/strings.ts
@@ -1,4 +1,3 @@
-
/* eslint-disable */
// Below is the regex to convert this into a csv for the Google Sheet
// export const (.*) = .*?(['|"|`])(.*)\2;
diff --git a/src/routes/suggestedPeopleOnboarding/SuggestedPeopleOnboardingStackNavigator.tsx b/src/routes/suggestedPeopleOnboarding/SuggestedPeopleOnboardingStackNavigator.tsx
new file mode 100644
index 00000000..e957e48c
--- /dev/null
+++ b/src/routes/suggestedPeopleOnboarding/SuggestedPeopleOnboardingStackNavigator.tsx
@@ -0,0 +1,8 @@
+import {createStackNavigator} from '@react-navigation/stack';
+
+export type SuggestedPeopleOnboardingStackParams = {
+ WelcomeScreen: undefined;
+ UploadPicture: undefined;
+};
+
+export const SuggestedPeopleOnboardingStack = createStackNavigator<SuggestedPeopleOnboardingStackParams>();
diff --git a/src/routes/suggestedPeopleOnboarding/SuggestedPeopleOnboardingStackScreen.tsx b/src/routes/suggestedPeopleOnboarding/SuggestedPeopleOnboardingStackScreen.tsx
new file mode 100644
index 00000000..970982c4
--- /dev/null
+++ b/src/routes/suggestedPeopleOnboarding/SuggestedPeopleOnboardingStackScreen.tsx
@@ -0,0 +1,36 @@
+import React from 'react';
+import {SuggestedPeopleOnboardingStack} from '.';
+import {
+ SuggestedPeopleWelcomeScreen,
+ SuggestedPeopleUploadPictureScreen,
+} from '../../screens';
+import {SCREEN_WIDTH} from '../../utils';
+import {headerBarOptions} from '../main';
+
+const SuggestedPeopleOnboardingStackScreen: React.FC = () => {
+ return (
+ <SuggestedPeopleOnboardingStack.Navigator
+ initialRouteName="WelcomeScreen"
+ screenOptions={{
+ headerShown: false,
+ gestureResponseDistance: {horizontal: SCREEN_WIDTH * 0.6},
+ }}>
+ <SuggestedPeopleOnboardingStack.Screen
+ name="WelcomeScreen"
+ component={SuggestedPeopleWelcomeScreen}
+ options={{
+ ...headerBarOptions('white', ''),
+ }}
+ />
+ <SuggestedPeopleOnboardingStack.Screen
+ name="UploadPicture"
+ component={SuggestedPeopleUploadPictureScreen}
+ options={{
+ ...headerBarOptions('white', ''),
+ }}
+ />
+ </SuggestedPeopleOnboardingStack.Navigator>
+ );
+};
+
+export default SuggestedPeopleOnboardingStackScreen;
diff --git a/src/routes/suggestedPeopleOnboarding/index.ts b/src/routes/suggestedPeopleOnboarding/index.ts
new file mode 100644
index 00000000..df711493
--- /dev/null
+++ b/src/routes/suggestedPeopleOnboarding/index.ts
@@ -0,0 +1,2 @@
+export * from './SuggestedPeopleOnboardingStackNavigator';
+export * from './SuggestedPeopleOnboardingStackScreen';
diff --git a/src/screens/index.ts b/src/screens/index.ts
index c34cd571..faf3d0b7 100644
--- a/src/screens/index.ts
+++ b/src/screens/index.ts
@@ -3,3 +3,4 @@ export * from './onboarding';
export * from './profile';
export * from './search';
export * from './suggestedPeople';
+export * from './suggestedPeopleOnboarding';
diff --git a/src/screens/suggestedPeople/AnimatedTutorial.tsx b/src/screens/suggestedPeople/AnimatedTutorial.tsx
index 8ebdaea6..bf34ba6e 100644
--- a/src/screens/suggestedPeople/AnimatedTutorial.tsx
+++ b/src/screens/suggestedPeople/AnimatedTutorial.tsx
@@ -1,13 +1,13 @@
-import * as React from 'react';
-import CloseIcon from '../../assets/ionicons/close-outline.svg';
+import {useNavigation} from '@react-navigation/native';
+import React from 'react';
import {StyleSheet, Text, View} from 'react-native';
import {Image} from 'react-native-animatable';
-import {isIPhoneX, SCREEN_WIDTH} from '../../utils';
import {SafeAreaView} from 'react-native-safe-area-context';
-import {useNavigation} from '@react-navigation/native';
import {useDispatch, useSelector} from 'react-redux';
+import CloseIcon from '../../assets/ionicons/close-outline.svg';
+import {suggestedPeopleAnimatedTutorialFinished} from '../../store/actions/user';
import {RootState} from '../../store/rootReducer';
-import {updateSPSwipeTutorial} from '../../store/actions/user';
+import {isIPhoneX, SCREEN_WIDTH} from '../../utils';
const AnimatedTutorial: React.FC = () => {
const navigation = useNavigation();
@@ -15,11 +15,7 @@ const AnimatedTutorial: React.FC = () => {
const {user} = useSelector((state: RootState) => state.user);
const handleCloseAnimationTutorial = async () => {
- /* In user's store, check if profile.sp_swipe_tutorial === 0
- * Make call to edit profile endpoint with suggested people === 1
- */
- const data = 1;
- dispatch(updateSPSwipeTutorial(user, data));
+ dispatch(suggestedPeopleAnimatedTutorialFinished(user.userId));
navigation.pop();
};
return (
diff --git a/src/screens/suggestedPeople/SuggestedPeopleScreen.tsx b/src/screens/suggestedPeople/SuggestedPeopleScreen.tsx
index 4d8607a4..3437cd85 100644
--- a/src/screens/suggestedPeople/SuggestedPeopleScreen.tsx
+++ b/src/screens/suggestedPeople/SuggestedPeopleScreen.tsx
@@ -1,3 +1,4 @@
+import {useFocusEffect, useNavigation} from '@react-navigation/native';
import React, {useCallback} from 'react';
import {
StatusBar,
@@ -7,16 +8,15 @@ import {
View,
} from 'react-native';
import {Image} from 'react-native-animatable';
-import {isIPhoneX, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
-import {TabsGradient, TaggsBar} from '../../components';
-import {SafeAreaView} from 'react-native-safe-area-context';
-import {normalize} from '../../utils';
import Animated from 'react-native-reanimated';
-import {ScreenType} from '../../types';
+import {SafeAreaView} from 'react-native-safe-area-context';
import {useSelector} from 'react-redux';
-import {RootState} from '../../store/rootReducer';
-import {useFocusEffect, useNavigation} from '@react-navigation/native';
+import {TabsGradient, TaggsBar} from '../../components';
import {MutualFriends} from '../../components/suggestedPeople';
+import SuggestedPeopleOnboardingStackScreen from '../../routes/suggestedPeopleOnboarding/SuggestedPeopleOnboardingStackScreen';
+import {RootState} from '../../store/rootReducer';
+import {ScreenType} from '../../types';
+import {isIPhoneX, normalize, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
/**
* Bare bones for suggested people consisting of:
@@ -24,75 +24,71 @@ import {MutualFriends} from '../../components/suggestedPeople';
*/
const SuggestedPeopleScreen: React.FC = () => {
+ const {suggested_people_linked} = useSelector(
+ (state: RootState) => state.user.profile,
+ ) ?? {suggested_people_linked: -1};
+ const y = Animated.useValue(0);
+
// Can be removed once firstname, username props are received
const firstName = 'Sarah';
// Adviced to maintain username as a variable here to append @ symbol for maintainability
const username = '@' + 'sarahmiller';
const navigation = useNavigation();
- const screenType = ScreenType.SuggestedPeople;
- const {
- profile: {sp_swipe_tutorial},
- } = useSelector((state: RootState) => state.user);
useFocusEffect(
useCallback(() => {
const navigateToAnimatedTutorial = () => {
- /* In user's store, check if profile.sp_swipe_tutorial === 0
- * If, true show tutorial.
- */
- if (sp_swipe_tutorial === 0) {
+ // if the user has finished the previous SP onboarding
+ if (suggested_people_linked === 1) {
navigation.navigate('AnimatedTutorial');
}
};
navigateToAnimatedTutorial();
- }, [sp_swipe_tutorial, navigation]),
+ }, [navigation, suggested_people_linked]),
);
- return (
- <>
- <SafeAreaView>
- <StatusBar barStyle={'light-content'} />
- <Image
- // !!! Displaying Sarah Miller's image
- source={require('../../assets/images/sarah_miller_full.jpeg')}
- style={styles.image}
- />
- <View style={styles.mainContainer}>
- <Text style={styles.title}>Suggested People</Text>
- <View style={styles.body}>
- {/* First row contaning name, username, add button (w/o functionality) */}
- <View style={styles.addUserContainer}>
- <View style={styles.nameInfoContainer}>
- <Text style={styles.firstName}>{firstName}</Text>
- <Text style={styles.username}>{username}</Text>
- </View>
- <TouchableOpacity
- activeOpacity={0.5}
- // TODO: Call function to Add Friend
- onPress={() => console.log('Call add friend function')}>
- <View style={styles.addButton}>
- <Text style={styles.addButtonTitle}>{'Add Friend'}</Text>
- </View>
- </TouchableOpacity>
+ const mainContent = () => (
+ <SafeAreaView>
+ <StatusBar barStyle={'light-content'} />
+ <Image
+ // !!! Displaying Sarah Miller's image
+ source={require('../../assets/images/sarah_miller_full.jpeg')}
+ style={styles.image}
+ />
+ <View style={styles.mainContainer}>
+ <Text style={styles.title}>Suggested People</Text>
+ <View style={styles.body}>
+ <View style={styles.addUserContainer}>
+ <View style={styles.nameInfoContainer}>
+ <Text style={styles.firstName}>{firstName}</Text>
+ <Text style={styles.username}>{username}</Text>
</View>
- {/* Taggs Bar. Displays only linked profiles for user while viewing their own profile. */}
- <TaggsBar
- y={Animated.useValue(0)}
- // :: For testing purposes, to view user's own profile, pass userXId=''
- userXId={''}
- profileBodyHeight={0}
- screenType={screenType}
- />
- {/* TODO: Pass mutual friends to component and render only if mutual friends exist / display no mutual friends
- * Needs to be displayed only if userX !user himself
- */}
- <MutualFriends />
+ <TouchableOpacity
+ activeOpacity={0.5}
+ onPress={() => console.log('Call add friend function')}>
+ <View style={styles.addButton}>
+ <Text style={styles.addButtonTitle}>{'Add Friend'}</Text>
+ </View>
+ </TouchableOpacity>
</View>
+ <TaggsBar
+ y={y}
+ userXId={undefined}
+ profileBodyHeight={0}
+ screenType={ScreenType.SuggestedPeople}
+ />
+ <MutualFriends />
</View>
- <TabsGradient />
- </SafeAreaView>
- </>
+ </View>
+ <TabsGradient />
+ </SafeAreaView>
+ );
+
+ return suggested_people_linked === 0 ? (
+ <SuggestedPeopleOnboardingStackScreen />
+ ) : (
+ mainContent()
);
};
diff --git a/src/screens/suggestedPeopleOnboarding/SuggestedPeopleUploadPictureScreen.tsx b/src/screens/suggestedPeopleOnboarding/SuggestedPeopleUploadPictureScreen.tsx
new file mode 100644
index 00000000..1b30c72f
--- /dev/null
+++ b/src/screens/suggestedPeopleOnboarding/SuggestedPeopleUploadPictureScreen.tsx
@@ -0,0 +1,164 @@
+import React, {useState} from 'react';
+import {
+ Alert,
+ Image,
+ ImageBackground,
+ StatusBar,
+ StyleSheet,
+} from 'react-native';
+import {Text} from 'react-native-animatable';
+import {TouchableOpacity} from 'react-native-gesture-handler';
+import ImagePicker from 'react-native-image-crop-picker';
+import {SafeAreaView} from 'react-native-safe-area-context';
+import {useDispatch} from 'react-redux';
+import {TaggSquareButton} from '../../components';
+import TaggLoadingIndicator from '../../components/common/TaggLoadingIndicator';
+import {SP_HEIGHT, SP_WIDTH} from '../../constants';
+import {ERROR_UPLOAD} from '../../constants/strings';
+import {sendSuggestedPeoplePhoto} from '../../services';
+import {uploadedSuggestedPeoplePhoto} from '../../store/actions';
+import {normalize, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
+
+const SuggestedPeopleUploadPictureScreen: React.FC = () => {
+ const [image, setImage] = useState<string | undefined>(undefined);
+ const [loading, setLoading] = useState(false);
+ const dispatch = useDispatch();
+
+ const openImagePicker = () => {
+ ImagePicker.openPicker({
+ smartAlbums: [
+ 'Favorites',
+ 'RecentlyAdded',
+ 'SelfPortraits',
+ 'Screenshots',
+ 'UserLibrary',
+ ],
+ width: SP_WIDTH,
+ height: SP_HEIGHT,
+ cropping: true,
+ cropperToolbarTitle: 'Select Photo',
+ mediaType: 'photo',
+ })
+ .then((picture) => {
+ if ('path' in picture) {
+ setImage(picture.path);
+ }
+ })
+ .catch((_) => {});
+ };
+
+ const uploadImage = async () => {
+ setLoading(true);
+ if (image) {
+ const success = await sendSuggestedPeoplePhoto(image);
+ if (success) {
+ dispatch(uploadedSuggestedPeoplePhoto());
+ } else {
+ Alert.alert(ERROR_UPLOAD);
+ }
+ }
+ setLoading(false);
+ };
+
+ return (
+ <>
+ {loading && <TaggLoadingIndicator fullscreen />}
+ <StatusBar barStyle={'light-content'} />
+ <SafeAreaView style={styles.container}>
+ <Text style={styles.title}>PHOTO</Text>
+ {image ? (
+ <Text style={styles.body}>Tap again to choose another photo</Text>
+ ) : (
+ <Text style={styles.body}>
+ Upload a photo, this is what other users will see
+ </Text>
+ )}
+ {image ? (
+ <TouchableOpacity onPress={openImagePicker}>
+ <ImageBackground
+ source={{uri: image}}
+ style={[styles.imageContainer, styles.overlay]}
+ borderRadius={30}>
+ <Image
+ style={styles.overlay}
+ source={require('../../assets/images/suggested-people-preview-silhouette.png')}
+ />
+ </ImageBackground>
+ </TouchableOpacity>
+ ) : (
+ <TouchableOpacity onPress={openImagePicker}>
+ <ImageBackground
+ source={require('../../assets/images/suggested-people-preview-silhouette.png')}
+ style={[styles.imageContainer, styles.overlay]}>
+ <Image
+ style={styles.images}
+ source={require('../../assets/images/images.png')}
+ />
+ <Text style={styles.body}>Upload Photo</Text>
+ </ImageBackground>
+ </TouchableOpacity>
+ )}
+ {image && (
+ <TaggSquareButton
+ onPress={uploadImage}
+ title={'Done'}
+ buttonStyle={'normal'}
+ buttonColor={'purple'}
+ labelColor={'white'}
+ style={styles.buttonStyle}
+ labelStyle={styles.buttonLabel}
+ />
+ )}
+ </SafeAreaView>
+ </>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ width: '100%',
+ height: '100%',
+ backgroundColor: '#878787',
+ alignItems: 'center',
+ },
+ title: {
+ marginTop: '5%',
+ fontSize: normalize(25),
+ lineHeight: normalize(30),
+ fontWeight: '600',
+ color: 'white',
+ },
+ body: {
+ fontSize: normalize(15),
+ lineHeight: normalize(18),
+ textAlign: 'center',
+ fontWeight: '600',
+ color: 'white',
+ marginTop: '5%',
+ width: SCREEN_WIDTH * 0.7,
+ },
+ buttonLabel: {
+ fontWeight: '600',
+ fontSize: normalize(15),
+ },
+ buttonStyle: {
+ width: '40%',
+ },
+ imageContainer: {
+ marginTop: '10%',
+ backgroundColor: 'black',
+ borderRadius: 30,
+ alignItems: 'center',
+ },
+ overlay: {
+ height: SCREEN_HEIGHT * 0.6,
+ aspectRatio: SP_WIDTH / SP_HEIGHT,
+ },
+ images: {
+ width: normalize(100),
+ height: normalize(100),
+ marginTop: '30%',
+ marginBottom: '10%',
+ },
+});
+export default SuggestedPeopleUploadPictureScreen;
diff --git a/src/screens/suggestedPeopleOnboarding/SuggestedPeopleWelcomeScreen.tsx b/src/screens/suggestedPeopleOnboarding/SuggestedPeopleWelcomeScreen.tsx
new file mode 100644
index 00000000..10f3b3a5
--- /dev/null
+++ b/src/screens/suggestedPeopleOnboarding/SuggestedPeopleWelcomeScreen.tsx
@@ -0,0 +1,81 @@
+import {BlurView} from '@react-native-community/blur';
+import {useNavigation} from '@react-navigation/native';
+import React from 'react';
+import {Image, StatusBar, StyleSheet, TouchableOpacity} from 'react-native';
+import {Text} from 'react-native-animatable';
+import {SafeAreaView} from 'react-native-safe-area-context';
+import UpArrow from '../../assets/icons/up_arrow.svg';
+import {isIPhoneX, normalize, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
+
+const SuggestedPeopleWelcomeScreen: React.FC = () => {
+ const navigation = useNavigation();
+ return (
+ <>
+ <StatusBar barStyle={'light-content'} />
+ <Image
+ style={styles.backgroundImage}
+ source={
+ isIPhoneX()
+ ? require('../../assets/images/suggested-people-preview-large.png')
+ : require('../../assets/images/suggested-people-preview-small.png')
+ }
+ resizeMode={'cover'}
+ />
+ <BlurView blurType={'light'} blurAmount={1}>
+ <SafeAreaView style={styles.container}>
+ <Image
+ style={styles.logo}
+ source={require('../../assets/images/tagg-logo.png')}
+ />
+ <Text style={styles.body}>
+ Welcome to the suggested people's page where you can discover new
+ people!{'\n\n'}Let's get you set up!
+ </Text>
+ </SafeAreaView>
+ </BlurView>
+ <TouchableOpacity
+ style={styles.nextButton}
+ onPress={() => navigation.push('UploadPicture')}>
+ <UpArrow color={'white'} />
+ </TouchableOpacity>
+ </>
+ );
+};
+
+const styles = StyleSheet.create({
+ backgroundImage: {
+ width: SCREEN_WIDTH,
+ height: SCREEN_HEIGHT,
+ position: 'absolute',
+ },
+ container: {
+ width: '100%',
+ height: '100%',
+ backgroundColor: 'rgba(0,0,0,0.65)',
+ alignItems: 'center',
+ },
+ logo: {
+ width: normalize(120),
+ height: normalize(120),
+ marginTop: '25%',
+ },
+ body: {
+ fontSize: normalize(20),
+ lineHeight: normalize(25),
+ textAlign: 'center',
+ fontWeight: '700',
+ color: 'white',
+ marginTop: '5%',
+ width: SCREEN_WIDTH * 0.8,
+ },
+ nextButton: {
+ position: 'absolute',
+ color: 'white',
+ transform: [{rotate: '90deg'}],
+ width: normalize(70),
+ aspectRatio: 0.86,
+ bottom: SCREEN_HEIGHT * 0.1,
+ right: '5%',
+ },
+});
+export default SuggestedPeopleWelcomeScreen;
diff --git a/src/screens/suggestedPeopleOnboarding/index.ts b/src/screens/suggestedPeopleOnboarding/index.ts
new file mode 100644
index 00000000..6bd9f1b2
--- /dev/null
+++ b/src/screens/suggestedPeopleOnboarding/index.ts
@@ -0,0 +1,2 @@
+export {default as SuggestedPeopleWelcomeScreen} from './SuggestedPeopleWelcomeScreen';
+export {default as SuggestedPeopleUploadPictureScreen} from './SuggestedPeopleUploadPictureScreen';
diff --git a/src/services/SuggestedPeopleService.ts b/src/services/SuggestedPeopleService.ts
new file mode 100644
index 00000000..7e43c3b6
--- /dev/null
+++ b/src/services/SuggestedPeopleService.ts
@@ -0,0 +1,50 @@
+import AsyncStorage from '@react-native-community/async-storage';
+import {EDIT_PROFILE_ENDPOINT, SP_UPDATE_PICTURE} from '../constants';
+
+export const sendSuggestedPeopleLinked = async (
+ userId: string,
+ stage: number,
+) => {
+ try {
+ const request = new FormData();
+ request.append('suggested_people_linked', stage);
+ const endpoint = EDIT_PROFILE_ENDPOINT + `${userId}/`;
+ const token = await AsyncStorage.getItem('token');
+ let response = await fetch(endpoint, {
+ method: 'PATCH',
+ headers: {
+ 'Content-Type': 'multipart/form-data',
+ Authorization: 'Token ' + token,
+ },
+ body: request,
+ });
+ return response.status === 200;
+ } catch (error) {
+ console.log('Error updating animated tutorial close button press');
+ return false;
+ }
+};
+
+export const sendSuggestedPeoplePhoto = async (photoUri: string) => {
+ try {
+ const token = await AsyncStorage.getItem('token');
+ const form = new FormData();
+ form.append('suggested_people', {
+ uri: photoUri,
+ name: 'sp_photo.jpg',
+ type: 'image/jpg',
+ });
+ const response = await fetch(SP_UPDATE_PICTURE, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'multipart/form-data',
+ Authorization: 'Token ' + token,
+ },
+ body: form,
+ });
+ return response.status === 201;
+ } catch (error) {
+ console.log('Error uploading SP photo');
+ return false;
+ }
+};
diff --git a/src/services/UserProfileService.ts b/src/services/UserProfileService.ts
index 3bca66f3..fff35370 100644
--- a/src/services/UserProfileService.ts
+++ b/src/services/UserProfileService.ts
@@ -2,20 +2,18 @@ import AsyncStorage from '@react-native-community/async-storage';
import moment from 'moment';
import {Alert} from 'react-native';
import RNFetchBlob from 'rn-fetch-blob';
-import {SocialAccountType, UserType} from '../types';
import {
- PROFILE_PHOTO_ENDPOINT,
- HEADER_PHOTO_ENDPOINT,
GET_FB_POSTS_ENDPOINT,
GET_IG_POSTS_ENDPOINT,
GET_TWITTER_POSTS_ENDPOINT,
- PROFILE_INFO_ENDPOINT,
+ HEADER_PHOTO_ENDPOINT,
PASSWORD_RESET_ENDPOINT,
+ PROFILE_INFO_ENDPOINT,
+ PROFILE_PHOTO_ENDPOINT,
+ PROFILE_PHOTO_THUMBNAIL_ENDPOINT,
+ SEND_OTP_ENDPOINT,
TAGG_CUSTOMER_SUPPORT,
VERIFY_OTP_ENDPOINT,
- SEND_OTP_ENDPOINT,
- PROFILE_PHOTO_THUMBNAIL_ENDPOINT,
- EDIT_PROFILE_ENDPOINT,
} from '../constants';
import {
ERROR_DOUBLE_CHECK_CONNECTION,
@@ -28,6 +26,7 @@ import {
SUCCESS_PWD_RESET,
SUCCESS_VERIFICATION_CODE_SENT,
} from '../constants/strings';
+import {SocialAccountType} from '../types';
export const loadProfileInfo = async (token: string, userId: string) => {
try {
@@ -50,15 +49,11 @@ export const loadProfileInfo = async (token: string, userId: string) => {
tiktok,
university_class,
profile_completion_stage,
- sp_swipe_tutorial,
+ suggested_people_linked,
friendship_status,
friendship_requester_id,
} = info;
birthday = birthday && moment(birthday).format('YYYY-MM-DD');
- console.log(
- 'Suggested People loaded from backend for logged in user: ',
- sp_swipe_tutorial,
- );
return {
name,
biography,
@@ -69,7 +64,7 @@ export const loadProfileInfo = async (token: string, userId: string) => {
tiktok,
university_class,
profile_completion_stage,
- sp_swipe_tutorial,
+ suggested_people_linked,
friendship_status,
friendship_requester_id,
};
@@ -320,27 +315,3 @@ export const sendOtp = async (phone: string) => {
return false;
}
};
-
-export const editSPSwipeTutorial = async (user: UserType) => {
- try {
- const request = new FormData();
- request.append('sp_swipe_tutorial', 1);
- const endpoint = EDIT_PROFILE_ENDPOINT + `${user.userId}/`;
- const token = await AsyncStorage.getItem('token');
- let response = await fetch(endpoint, {
- method: 'PATCH',
- headers: {
- 'Content-Type': 'multipart/form-data',
- Authorization: 'Token ' + token,
- },
- body: request,
- });
- if (response.status === 200) {
- return true;
- } else {
- return false;
- }
- } catch (error) {
- console.log('Error updating animated tutorial close button press');
- }
-};
diff --git a/src/services/index.ts b/src/services/index.ts
index 9c168d4f..ef71233a 100644
--- a/src/services/index.ts
+++ b/src/services/index.ts
@@ -11,3 +11,4 @@ export * from './FCMService';
export * from './WaitlistUserService';
export * from './CommonService';
export * from './CommentService';
+export * from './SuggestedPeopleService';
diff --git a/src/store/actions/user.ts b/src/store/actions/user.ts
index 990f9260..ef134dc5 100644
--- a/src/store/actions/user.ts
+++ b/src/store/actions/user.ts
@@ -1,9 +1,9 @@
import {Action, ThunkAction} from '@reduxjs/toolkit';
import {
- editSPSwipeTutorial,
loadAvatar,
loadCover,
loadProfileInfo,
+ sendSuggestedPeopleLinked,
} from '../../services';
import {UserType} from '../../types/types';
import {getTokenOrLogout} from '../../utils';
@@ -13,11 +13,11 @@ import {
setNewNotificationReceived,
setNewVersionAvailable,
setReplyPosted,
+ setSuggestedPeopleLinked,
socialEdited,
userDetailsFetched,
userLoggedIn,
} from '../reducers';
-import {spSwipeTutorialUpdated} from '../reducers/userReducer';
import {RootState} from '../rootReducer';
import {CommentThreadType} from './../../types/types';
@@ -163,9 +163,24 @@ export const logout = (): ThunkAction<
}
};
-export const updateSPSwipeTutorial = (
- user: UserType,
- data: number,
+export const uploadedSuggestedPeoplePhoto = (): ThunkAction<
+ Promise<void>,
+ RootState,
+ unknown,
+ Action<string>
+> => async (dispatch) => {
+ try {
+ dispatch({
+ type: setSuggestedPeopleLinked.type,
+ payload: {stage: 1},
+ });
+ } catch (error) {
+ console.log(error);
+ }
+};
+
+export const suggestedPeopleAnimatedTutorialFinished = (
+ userId: string,
): ThunkAction<
Promise<boolean | undefined>,
RootState,
@@ -173,12 +188,13 @@ export const updateSPSwipeTutorial = (
Action<string>
> => async (dispatch) => {
try {
- // update store first, assume success
+ // update store first, assume request is successful
dispatch({
- type: spSwipeTutorialUpdated.type,
- payload: {sp_swipe_tutorial: data},
+ type: setSuggestedPeopleLinked.type,
+ payload: {stage: 2},
});
- return await editSPSwipeTutorial(user);
+ // need to tell the server that the stage is now 2
+ return await sendSuggestedPeopleLinked(userId, 2);
} catch (error) {
console.log('Error while updating suggested people linked state: ', error);
}
diff --git a/src/store/initialStates.ts b/src/store/initialStates.ts
index 93b1bc6e..10fdad25 100644
--- a/src/store/initialStates.ts
+++ b/src/store/initialStates.ts
@@ -21,7 +21,7 @@ export const NO_PROFILE: ProfileType = {
//Default to an invalid value and ignore it gracefully while showing tutorials / popups.
profile_completion_stage: -1,
- sp_swipe_tutorial: 0,
+ suggested_people_linked: -1,
snapchat: '',
tiktok: '',
friendship_status: 'no_record',
diff --git a/src/store/reducers/userReducer.ts b/src/store/reducers/userReducer.ts
index 773977db..5203fa3c 100644
--- a/src/store/reducers/userReducer.ts
+++ b/src/store/reducers/userReducer.ts
@@ -46,8 +46,8 @@ const userDataSlice = createSlice({
state.profile.profile_completion_stage = action.payload.stage;
},
- spSwipeTutorialUpdated: (state, action) => {
- state.profile.sp_swipe_tutorial = action.payload.sp_swipe_tutorial;
+ setSuggestedPeopleLinked: (state, action) => {
+ state.profile.suggested_people_linked = action.payload.stage;
},
setIsOnboardedUser: (state, action) => {
@@ -73,10 +73,10 @@ export const {
userDetailsFetched,
socialEdited,
profileCompletionStageUpdated,
+ setSuggestedPeopleLinked,
setIsOnboardedUser,
setNewVersionAvailable,
setNewNotificationReceived,
setReplyPosted,
- spSwipeTutorialUpdated,
} = userDataSlice.actions;
export const userDataReducer = userDataSlice.reducer;
diff --git a/src/types/types.ts b/src/types/types.ts
index 874b9eb7..3c17cfa4 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -23,7 +23,7 @@ export interface ProfileType {
gender: string;
university_class: number;
profile_completion_stage: number;
- sp_swipe_tutorial: number;
+ suggested_people_linked: number;
birthday: Date | undefined;
snapchat: string;
tiktok: string;