diff options
author | Ivan Chen <ivan@tagg.id> | 2021-02-26 21:16:02 -0500 |
---|---|---|
committer | Ivan Chen <ivan@tagg.id> | 2021-02-26 21:16:02 -0500 |
commit | 7faeb487da4fac1e57d8d147da1e41cac16bb28d (patch) | |
tree | 6e49e419200c670a8de1dd04c67a251acac97a1c /src/screens/onboarding/OnboardingStepThree.tsx | |
parent | bd3e189405e013b847b74fbad66543f6368a4ec8 (diff) |
onboarding revamp done!
Diffstat (limited to 'src/screens/onboarding/OnboardingStepThree.tsx')
-rw-r--r-- | src/screens/onboarding/OnboardingStepThree.tsx | 411 |
1 files changed, 411 insertions, 0 deletions
diff --git a/src/screens/onboarding/OnboardingStepThree.tsx b/src/screens/onboarding/OnboardingStepThree.tsx new file mode 100644 index 00000000..f832539d --- /dev/null +++ b/src/screens/onboarding/OnboardingStepThree.tsx @@ -0,0 +1,411 @@ +import AsyncStorage from '@react-native-community/async-storage'; +import {RouteProp} from '@react-navigation/native'; +import {StackNavigationProp} from '@react-navigation/stack'; +import moment from 'moment'; +import React, {useMemo} from 'react'; +import { + Alert, + Image, + StatusBar, + StyleSheet, + Text, + TouchableOpacity, + View, +} from 'react-native'; +import ImagePicker from 'react-native-image-crop-picker'; +import Animated from 'react-native-reanimated'; +import { + Background, + BirthDatePicker, + RegistrationWizard, + TaggDropDown, + TaggInput, +} from '../../components'; +import { + CLASS_YEAR_LIST, + EDIT_PROFILE_ENDPOINT, + genderRegex, + TAGG_PURPLE, +} from '../../constants'; +import { + ERROR_DOUBLE_CHECK_CONNECTION, + ERROR_PROFILE_CREATION_SHORT, + ERROR_SELECT_BIRTHDAY, + ERROR_SELECT_CLASS_YEAR, + ERROR_SELECT_GENDER, + ERROR_SOMETHING_WENT_WRONG_REFRESH, + ERROR_UPLOAD_SMALL_PROFILE_PIC, +} from '../../constants/strings'; +import {OnboardingStackParams} from '../../routes/onboarding'; +import {BackgroundGradientType} from '../../types'; +import {normalize, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; + +type OnboardingStepThreeRouteProp = RouteProp< + OnboardingStackParams, + 'OnboardingStepThree' +>; +type OnboardingStepThreeNavigationProp = StackNavigationProp< + OnboardingStackParams, + 'OnboardingStepThree' +>; +interface OnboardingStepThreeProps { + route: OnboardingStepThreeRouteProp; + navigation: OnboardingStepThreeNavigationProp; +} + +const OnboardingStepThree: React.FC<OnboardingStepThreeProps> = ({ + route, + navigation, +}) => { + const {userId, username} = route.params; + let emptyDate: string | undefined; + const [form, setForm] = React.useState({ + smallPic: '', + birthdate: emptyDate, + gender: '', + isValidGender: true, + classYear: -1, + attemptedSubmit: false, + }); + const [customGender, setCustomGender] = React.useState(false); + + const classYearList = CLASS_YEAR_LIST.map((value) => ({ + label: value, + value, + })); + + /** + * Profile screen "Add profile picture" button + */ + const SmallProfilePic = () => ( + <TouchableOpacity + accessible={true} + accessibilityLabel="ADD PROFILE PICTURE" + onPress={goToGallerySmallPic} + style={styles.smallProfileUploader}> + {form.smallPic ? ( + <Image source={{uri: form.smallPic}} style={styles.smallProfilePic} /> + ) : ( + <Text style={styles.smallProfileText}>ADD PROFILE PICTURE</Text> + )} + </TouchableOpacity> + ); + + const goToGallerySmallPic = () => { + ImagePicker.openPicker({ + smartAlbums: [ + 'Favorites', + 'RecentlyAdded', + 'SelfPortraits', + 'Screenshots', + 'UserLibrary', + ], + width: 580, + height: 580, + cropping: true, + cropperToolbarTitle: 'Select Profile Picture', + mediaType: 'photo', + cropperCircleOverlay: true, + }).then((picture) => { + if ('path' in picture) { + setForm({ + ...form, + smallPic: picture.path, + }); + } + }); + }; + + const handleGenderUpdate = (gender: string) => { + if (gender === 'custom') { + setCustomGender(true); + } else { + setCustomGender(false); + let isValidGender: boolean = true; + setForm({ + ...form, + gender, + isValidGender, + }); + } + }; + + const handleClassYearUpdate = (value: string) => { + console.log('foooooo'); + const classYear = Number.parseInt(value); + setForm({ + ...form, + classYear, + }); + }; + + const handleCustomGenderUpdate = (gender: string) => { + let isValidGender: boolean = genderRegex.test(gender); + gender = gender.replace(' ', '-'); + setForm({ + ...form, + gender, + isValidGender, + }); + }; + + const handleBirthdateUpdate = (birthdate: Date) => { + setForm({ + ...form, + birthdate: birthdate && moment(birthdate).format('YYYY-MM-DD'), + }); + }; + + const handleSubmit = async () => { + if (!form.smallPic) { + Alert.alert(ERROR_UPLOAD_SMALL_PROFILE_PIC); + return; + } + if (form.classYear === -1) { + Alert.alert(ERROR_SELECT_CLASS_YEAR); + return; + } + if (form.birthdate === emptyDate) { + Alert.alert(ERROR_SELECT_BIRTHDAY); + return; + } + if (form.gender === '') { + Alert.alert(ERROR_SELECT_GENDER); + return; + } + if (!form.attemptedSubmit) { + setForm({ + ...form, + attemptedSubmit: true, + }); + } + let invalidFields: boolean = false; + const request = new FormData(); + if (form.smallPic) { + request.append('smallProfilePicture', { + uri: form.smallPic, + name: 'small_profile_pic.jpg', + type: 'image/jpg', + }); + } + + if (form.birthdate) { + request.append('birthday', form.birthdate); + } + + if (customGender) { + if (form.isValidGender) { + request.append('gender', form.gender); + } else { + setForm({...form, attemptedSubmit: false}); + setTimeout(() => setForm({...form, attemptedSubmit: true})); + invalidFields = true; + } + } else { + if (form.isValidGender) { + request.append('gender', form.gender); + } + } + + if (form.classYear !== -1) { + request.append('university_class', form.classYear); + } + + if (invalidFields) { + return; + } + + const endpoint = EDIT_PROFILE_ENDPOINT + `${userId}/`; + try { + const token = await AsyncStorage.getItem('token'); + let response = await fetch(endpoint, { + method: 'PATCH', + headers: { + 'Content-Type': 'multipart/form-data', + Authorization: 'Token ' + token, + }, + body: request, + }); + console.log(route.params.userId); + let statusCode = response.status; + let data = await response.json(); + if (statusCode === 200) { + navigation.navigate('InvitationCodeVerification', { + userId: route.params.userId, + }); + } else if (statusCode === 400) { + Alert.alert( + 'Profile update failed. 😔', + data.error || 'Something went wrong! ðŸ˜', + ); + } else { + Alert.alert(ERROR_SOMETHING_WENT_WRONG_REFRESH); + } + } catch (error) { + Alert.alert(ERROR_PROFILE_CREATION_SHORT, ERROR_DOUBLE_CHECK_CONNECTION); + return { + name: 'Profile creation error', + description: error, + }; + } + }; + + const profilePics = useMemo(() => { + return ( + <View style={styles.profile}> + <SmallProfilePic /> + <Image + source={require('../../assets/icons/purple-plus.png')} + style={styles.purplePlus} + /> + </View> + ); + }, [form.largePic, form.smallPic]); + + return ( + <Animated.ScrollView bounces={false}> + <Background + centered + gradientType={BackgroundGradientType.Light} + style={styles.container}> + <StatusBar barStyle="light-content" /> + <RegistrationWizard style={styles.wizard} step="three" /> + {profilePics} + <View style={styles.contentContainer}> + <TaggDropDown + onValueChange={(value: string) => handleClassYearUpdate(value)} + items={classYearList} + placeholder={{ + label: 'Class Year', + value: null, + color: '#ddd', + }} + /> + <BirthDatePicker + handleBDUpdate={handleBirthdateUpdate} + width={280} + date={form.birthdate} + showPresetdate={false} + /> + {customGender && ( + <TaggInput + accessibilityHint="Custom" + accessibilityLabel="Gender input field." + placeholder="Enter your gender" + autoCompleteType="off" + textContentType="none" + autoCapitalize="none" + returnKeyType="next" + blurOnSubmit={false} + onChangeText={handleCustomGenderUpdate} + onSubmitEditing={() => handleSubmit()} + valid={form.isValidGender} + attemptedSubmit={form.attemptedSubmit} + invalidWarning={ + 'Custom field can only contain letters and hyphens' + } + width={280} + /> + )} + <TaggDropDown + onValueChange={(value: string) => handleGenderUpdate(value)} + items={[ + {label: 'Male', value: 'male'}, + {label: 'Female', value: 'female'}, + {label: 'Custom', value: 'custom'}, + ]} + placeholder={{ + label: 'Gender', + value: null, + color: '#ddd', + }} + /> + </View> + <View style={styles.footer}> + <TouchableOpacity onPress={handleSubmit} style={styles.submitBtn}> + <Text style={styles.submitBtnLabel}>Let's start!</Text> + </TouchableOpacity> + </View> + </Background> + </Animated.ScrollView> + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + height: SCREEN_HEIGHT, + }, + profile: { + marginTop: '10%', + marginBottom: '5%', + }, + contentContainer: { + position: 'relative', + width: 280, + }, + smallProfileUploader: { + justifyContent: 'center', + alignItems: 'center', + padding: 20, + backgroundColor: '#E1F0FF', + height: normalize(150), + width: normalize(150), + borderRadius: normalize(150), + }, + smallProfileText: { + textAlign: 'center', + fontSize: 14, + fontWeight: 'bold', + color: '#806DF4', + }, + smallProfilePic: { + height: normalize(150), + width: normalize(150), + borderRadius: normalize(150), + borderWidth: 2, + borderColor: 'white', + }, + submitBtn: { + backgroundColor: TAGG_PURPLE, + justifyContent: 'center', + alignItems: 'center', + width: SCREEN_WIDTH / 2.5, + height: SCREEN_WIDTH / 10, + borderRadius: 5, + marginTop: '5%', + alignSelf: 'center', + }, + submitBtnLabel: { + fontSize: 16, + fontWeight: '500', + color: '#fff', + }, + goBack: { + textDecorationLine: 'underline', + color: '#fff', + fontSize: 15, + fontWeight: '600', + }, + footer: { + marginTop: '3%', + alignItems: 'center', + justifyContent: 'space-around', + height: SCREEN_HEIGHT * 0.15, + }, + wizard: { + position: 'absolute', + top: SCREEN_HEIGHT * 0.1, + }, + purplePlus: { + position: 'absolute', + height: normalize(40), + width: normalize(40), + bottom: 0, + right: 0, + }, +}); + +export default OnboardingStepThree; |