diff options
author | Leon Jiang <35908040+leonyjiang@users.noreply.github.com> | 2020-07-20 16:27:45 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-20 19:27:45 -0400 |
commit | e87d4f2b10cff8cf5f31784cfddb22727a94f373 (patch) | |
tree | dd454883f3f4054e895c7ce80f2845d68ba31364 /src | |
parent | c38291fb50e30af0739f17de42f3b8bb51cca73e (diff) |
[TMA-134] Profile Picture Upload (#22)
* Fix styling, remove unnecessary state
* Change large profile to square crop, restyle
* Disable gestures on Verification and Profile screens
* Forward username and ID to Profile screen
* Add API request upon submit & filler components
* Restore gestures to Verification and Profile
* Rewrite network failure error message
* Update profile request form keys
Diffstat (limited to 'src')
-rw-r--r-- | src/routes/Routes.tsx | 4 | ||||
-rw-r--r-- | src/screens/onboarding/Login.tsx | 4 | ||||
-rw-r--r-- | src/screens/onboarding/Profile.tsx | 265 | ||||
-rw-r--r-- | src/screens/onboarding/Registration.tsx | 6 | ||||
-rw-r--r-- | src/screens/onboarding/Verification.tsx | 18 |
5 files changed, 172 insertions, 125 deletions
diff --git a/src/routes/Routes.tsx b/src/routes/Routes.tsx index 93c16fc9..63486448 100644 --- a/src/routes/Routes.tsx +++ b/src/routes/Routes.tsx @@ -11,8 +11,8 @@ import { export type RootStackParamList = { Login: undefined; Registration: undefined; - Verification: {username: string; email: string} | undefined; - Profile: undefined; + Verification: {username: string; email: string; userId: string}; + Profile: {username: string; userId: string}; }; const RootStack = createStackNavigator<RootStackParamList>(); diff --git a/src/screens/onboarding/Login.tsx b/src/screens/onboarding/Login.tsx index 8e406522..8aeb5e45 100644 --- a/src/screens/onboarding/Login.tsx +++ b/src/screens/onboarding/Login.tsx @@ -136,8 +136,8 @@ const Login: React.FC<LoginProps> = ({navigation}: LoginProps) => { } } catch (error) { Alert.alert( - 'Looks like our servers are down. 😓', - "Try again in a couple minutes. We're sorry for the inconvenience.", + 'Login failed 😓', + 'Please double-check your network connection and retry.', ); return { name: 'Login error', diff --git a/src/screens/onboarding/Profile.tsx b/src/screens/onboarding/Profile.tsx index cb4a2728..d42b1185 100644 --- a/src/screens/onboarding/Profile.tsx +++ b/src/screens/onboarding/Profile.tsx @@ -2,16 +2,18 @@ import React from 'react'; import {RouteProp} from '@react-navigation/native'; import {StackNavigationProp} from '@react-navigation/stack'; import { - View, Text, StatusBar, StyleSheet, Image, TouchableOpacity, + Alert, + View, } from 'react-native'; import {RootStackParamList} from '../../routes'; import {Background} from '../../components'; import ImagePicker from 'react-native-image-crop-picker'; +import {REGISTER_ENDPOINT} from '../../constants'; type ProfileScreenRouteProp = RouteProp<RootStackParamList, 'Profile'>; type ProfileScreenNavigationProp = StackNavigationProp< @@ -24,75 +26,53 @@ interface ProfileProps { } /** - * Create Profile screen for onboarding. - * @param navigation react-navigation navigation object. + * Create profile screen for onboarding. + * @param navigation react-navigation navigation object */ -const Profile: React.FC<ProfileProps> = ({}) => { - const [largePicture, setLargePicture] = React.useState({ - path: '', - height: 0, - width: 0, - }); - const [smallPicture, setSmallPicture] = React.useState({ - path: '', - height: 0, - width: 0, - }); - const [showLargeText, setShowLargeText] = React.useState(true); - const [showSmallText, setShowSmallText] = React.useState(true); +const Profile: React.FC<ProfileProps> = ({route}) => { + const {userId, username} = route.params; + const [largePic, setLargePic] = React.useState(''); + const [smallPic, setSmallPic] = React.useState(''); /** * Profile screen "Add Large Profile Pic Here" button */ const LargeProfilePic = () => ( - <View style={styles.container}> - <View style={styles.largeButton}> - <TouchableOpacity - accessible={true} - accessibilityLabel="ADD LARGE PROFILE PIC HERE" - onPress={goToGalleryLargePic} - style={styles.largeButtonContainer}> - {showLargeText ? ( - <Text style={styles.addLargeProfilePicText}> - ADD LARGE PROFILE PIC HERE - </Text> - ) : null} - - {largePicture.path !== '' && ( - <Image - source={{uri: largePicture.path}} - style={styles.largeProfilePic} - /> - )} - </TouchableOpacity> - </View> - </View> + <TouchableOpacity + accessible={true} + accessibilityLabel="ADD LARGE PROFILE PIC HERE" + onPress={goToGalleryLargePic} + style={styles.largeProfile}> + {largePic ? ( + <Image + source={{uri: largePic}} + style={[styles.largeProfile, styles.profilePic]} + /> + ) : ( + <Text style={styles.largeProfileText}>ADD LARGE PROFILE PIC HERE</Text> + )} + </TouchableOpacity> ); /** * Profile screen "Add Smaller Profile Pic Here" button */ const SmallProfilePic = () => ( - <View style={styles.container}> - <View style={styles.smallButton}> - <TouchableOpacity - accessible={true} - accessibilityLabel="ADD SMALLER PIC" - onPress={goToGallerySmallPic}> - {showSmallText ? ( - <Text style={styles.addSmallProfilePicText}>ADD SMALLER PIC</Text> - ) : null} - - {smallPicture.path !== '' && ( - <Image - source={{uri: smallPicture.path}} - style={styles.smallProfilePic} - /> - )} - </TouchableOpacity> - </View> - </View> + <TouchableOpacity + accessible={true} + accessibilityLabel="ADD SMALLER PIC" + onPress={goToGallerySmallPic} + style={styles.smallProfile}> + {smallPic ? ( + <Image + source={{uri: smallPic}} + style={[styles.smallProfile, styles.profilePic]} + /> + ) : ( + <Text style={styles.smallProfileText}>ADD SMALLER PIC</Text> + )} + </TouchableOpacity> ); /* @@ -104,16 +84,12 @@ const Profile: React.FC<ProfileProps> = ({}) => { width: 580, height: 580, cropping: true, - cropperCircleOverlay: true, + cropperToolbarTitle: 'Large profile picture', + mediaType: 'photo', }) .then((picture) => { if ('path' in picture) { - setLargePicture({ - path: picture.path, - height: picture.height, - width: picture.width, - }); - setShowLargeText(false); + setLargePic(picture.path); } }) .catch(() => {}); @@ -128,88 +104,149 @@ const Profile: React.FC<ProfileProps> = ({}) => { width: 580, height: 580, cropping: true, + cropperToolbarTitle: 'Small profile picture', + mediaType: 'photo', cropperCircleOverlay: true, }) .then((picture) => { if ('path' in picture) { - setSmallPicture({ - path: picture.path, - height: picture.height, - width: picture.width, - }); - setShowSmallText(false); + setSmallPic(picture.path); } }) .catch(() => {}); }; + const handleSubmit = async () => { + const form = new FormData(); + if (largePic) { + form.append('largeProfilePicture', { + uri: largePic, + name: 'large_profile_pic.jpg', + type: 'image/jpg', + }); + } + if (smallPic) { + form.append('smallProfilePicture', { + uri: smallPic, + name: 'small_profile_pic.jpg', + type: 'image/jpg', + }); + } + const endpoint = REGISTER_ENDPOINT + `${userId}/`; + try { + let response = await fetch(endpoint, { + method: 'PATCH', + headers: { + 'Content-Type': 'multipart/form-data', + }, + body: form, + }); + let data = await response.json(); + let statusCode = response.status; + if (statusCode === 200) { + Alert.alert( + 'Profile successfully created! 🥳', + `Welcome to Tagg, ${username}!`, + ); + } else if (statusCode === 400) { + Alert.alert('Profile update failed. 😔', `${data}`); + } else { + Alert.alert( + 'Something went wrong! ðŸ˜', + "Would you believe me if I told you that I don't know what happened?", + ); + } + } catch (error) { + Alert.alert( + 'Profile creation failed 😓', + 'Please double-check your network connection and retry.', + ); + return { + name: 'Profile creation error', + description: error, + }; + } + }; + return ( - <Background> + <Background centered> <StatusBar barStyle="light-content" /> <LargeProfilePic /> <SmallProfilePic /> + <View style={styles.dummyField}> + <Text>DUMMY WEBSITE</Text> + </View> + <View style={styles.dummyField}> + <Text>DUMMY BIO</Text> + </View> + <TouchableOpacity onPress={handleSubmit} style={styles.submitBtn}> + <Text style={styles.submitBtnLabel}>Let's start!</Text> + </TouchableOpacity> </Background> ); }; const styles = StyleSheet.create({ - container: { - flex: 1, - flexDirection: 'row', + largeProfile: { justifyContent: 'center', alignItems: 'center', - }, - largeButtonContainer: { - flex: 1, - justifyContent: 'center', - }, - largeButton: { - padding: 5, - height: 180, - width: 180, - borderRadius: 400, + padding: 15, + height: 230, + width: 230, + borderRadius: 23, backgroundColor: '#fff', - alignItems: 'center', - marginTop: '100%', - marginRight: '12%', - zIndex: 2, + marginRight: '6%', }, - addLargeProfilePicText: { + largeProfileText: { + textAlign: 'center', + fontSize: 14, fontWeight: 'bold', - padding: 22, - fontSize: 12, color: '#863FF9', - textAlign: 'center', }, - largeProfilePic: { - height: 180, - width: 180, - borderRadius: 400, - }, - smallButton: { - position: 'relative', - padding: 5, + smallProfile: { + justifyContent: 'center', + alignItems: 'center', + padding: 20, height: 110, width: 110, - borderRadius: 400, + borderRadius: 55, backgroundColor: '#E1F0FF', - justifyContent: 'center', - alignItems: 'center', - zIndex: 1, - marginTop: '128%', - marginLeft: '30%', + marginLeft: '45%', + bottom: '7%', }, - addSmallProfilePicText: { + smallProfileText: { + textAlign: 'center', + fontSize: 14, fontWeight: 'bold', - padding: 10, - fontSize: 12, color: '#806DF4', - textAlign: 'center', }, - smallProfilePic: { - height: 110, - width: 110, - borderRadius: 400, + profilePic: { + marginRight: 0, + marginLeft: 0, + bottom: 0, + }, + dummyField: { + height: '10%', + width: '80%', + justifyContent: 'center', + alignItems: 'center', + borderColor: '#fff', + borderWidth: 1, + borderRadius: 8, + marginBottom: '10%', + }, + submitBtn: { + backgroundColor: '#8F01FF', + justifyContent: 'center', + alignItems: 'center', + width: 150, + height: 40, + borderRadius: 5, + }, + submitBtnLabel: { + fontSize: 16, + fontWeight: '500', + color: '#fff', }, }); diff --git a/src/screens/onboarding/Registration.tsx b/src/screens/onboarding/Registration.tsx index 8d564cd0..6cad92a5 100644 --- a/src/screens/onboarding/Registration.tsx +++ b/src/screens/onboarding/Registration.tsx @@ -227,9 +227,11 @@ const Registration: React.FC<RegistrationProps> = ({navigation}) => { ); let otpStatusCode = sendOtpResponse.status; if (otpStatusCode === 200) { + const userId: string = data.UserID; navigation.navigate('Verification', { username: form.username, email: form.email, + userId: userId, }); } } else if (statusCode === 409) { @@ -252,8 +254,8 @@ const Registration: React.FC<RegistrationProps> = ({navigation}) => { } } catch (error) { Alert.alert( - 'Looks like our servers are down. 😓', - "Try again in a couple minutes. We're sorry for the inconvenience.", + 'Registration failed 😓', + 'Please double-check your network connection and retry.', ); return { name: 'Registration error', diff --git a/src/screens/onboarding/Verification.tsx b/src/screens/onboarding/Verification.tsx index 77a43a6d..baa5c6b3 100644 --- a/src/screens/onboarding/Verification.tsx +++ b/src/screens/onboarding/Verification.tsx @@ -42,9 +42,7 @@ const Verification: React.FC<VerificationProps> = ({route, navigation}) => { value, setValue, }); - const registrationVals = route.params; - const username: string = registrationVals!.username; - const email: string = registrationVals!.email; + const {username, email, userId} = route.params; /** * Sends the verify_otp request upon tapping the Verify button. @@ -61,7 +59,10 @@ const Verification: React.FC<VerificationProps> = ({route, navigation}) => { }); let statusCode = verifyOtpResponse.status; if (statusCode === 200) { - navigation.navigate('Profile'); + navigation.navigate('Profile', { + userId: userId, + username: username, + }); } else { Alert.alert( 'Invalid verification code 🤔', @@ -69,7 +70,14 @@ const Verification: React.FC<VerificationProps> = ({route, navigation}) => { ); } } catch (error) { - console.log(error); + Alert.alert( + 'Verifiation failed 😓', + 'Please double-check your network connection and retry.', + ); + return { + name: 'Verification error', + description: error, + }; } }; |