import {RouteProp} from '@react-navigation/native'; import {StackNavigationProp} from '@react-navigation/stack'; import React, {Fragment, useCallback, useEffect, useState} from 'react'; import { Alert, Image, Keyboard, KeyboardAvoidingView, Platform, SafeAreaView, StatusBar, StyleSheet, Text, TouchableOpacity, View, } from 'react-native'; import {Button} from 'react-native-elements'; import ImagePicker from 'react-native-image-crop-picker'; import Animated from 'react-native-reanimated'; import {useDispatch, useSelector} from 'react-redux'; import { Background, SocialIcon, TabsGradient, TaggBigInput, TaggDropDown, TaggInput, } from '../../components'; import TaggLoadingIndicator from '../../components/common/TaggLoadingIndicator'; import { bioRegex, CLASS_YEAR_LIST, genderRegex, websiteRegex, } from '../../constants'; import { ERROR_UPLOAD_LARGE_PROFILE_PIC, ERROR_UPLOAD_SMALL_PROFILE_PIC, } from '../../constants/strings'; import {MainStackParams} from '../../routes'; import {patchEditProfile} from '../../services'; import {loadUserData, resetHeaderAndProfileImage} from '../../store/actions'; import {RootState} from '../../store/rootReducer'; import {BackgroundGradientType, ScreenType} from '../../types'; import {HeaderHeight, SCREEN_HEIGHT} from '../../utils'; type EditProfileNavigationProp = StackNavigationProp< MainStackParams, 'EditProfile' >; interface EditProfileProps { route: RouteProp; navigation: EditProfileNavigationProp; } /** * Create profile screen for onboarding. * @param navigation react-navigation navigation object */ const EditProfile: React.FC = ({route, navigation}) => { const y: Animated.Value = Animated.useValue(0); const {userId, username} = route.params; const { profile: {website, biography, gender, snapchat, tiktok, university_class}, avatar, cover, } = useSelector((state: RootState) => state.user); const [needsUpdate, setNeedsUpdate] = useState(false); const [loading, setLoading] = useState(false); const dispatch = useDispatch(); useEffect(() => { if (needsUpdate) { dispatch(resetHeaderAndProfileImage()); dispatch(loadUserData({userId, username})); } }, [dispatch, needsUpdate, userId, username]); const [isCustomGender, setIsCustomGender] = React.useState( gender !== '' && gender !== 'female' && gender !== 'male', ); const [form, setForm] = React.useState({ largePic: cover ? cover : '', smallPic: avatar ? avatar : '', website: website ? website : '', bio: biography ? biography : '', gender: isCustomGender ? 'custom' : gender, customGenderText: isCustomGender ? gender : '', snapchat: snapchat, tiktok: tiktok, isValidWebsite: true, isValidBio: true, isValidGender: true, isValidSnapchat: true, isValidTiktok: true, attemptedSubmit: false, classYear: university_class, }); var classYearList: Array = []; CLASS_YEAR_LIST.map((value) => { classYearList.push({label: value, value: value}); }); /** * Profile screen "Add header image" button */ const LargeProfilePic = () => ( {form.largePic ? ( ) : ( ADD HEADER IMAGE )} ); /** * Profile screen "Add profile picture" button */ const SmallProfilePic = () => ( {form.smallPic ? ( ) : ( ADD PROFILE PICTURE )} ); const goToGalleryLargePic = () => { ImagePicker.openPicker({ smartAlbums: [ 'Favorites', 'RecentlyAdded', 'SelfPortraits', 'Screenshots', 'UserLibrary', ], width: 580, height: 580, cropping: true, cropperToolbarTitle: 'Select Header', mediaType: 'photo', }).then((picture) => { if ('path' in picture) { setForm({ ...form, largePic: picture.path, }); } }); }; 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, }); } }); }; /* * Handles changes to the website field value and verifies the input by updating state and running a validation function. */ const handleWebsiteUpdate = (newWebsite: string) => { newWebsite = newWebsite.trim(); let isValidWebsite: boolean = websiteRegex.test(newWebsite); setForm({ ...form, website: newWebsite, isValidWebsite, }); }; /* * Handles changes to the bio field value and verifies the input by updating state and running a validation function. */ const handleBioUpdate = (newBio: string) => { let isValidBio: boolean = bioRegex.test(newBio); setForm({ ...form, bio: newBio, isValidBio, }); }; const handleGenderUpdate = (newGender: string) => { if (newGender === 'custom') { setForm({...form, gender: newGender}); setIsCustomGender(true); } else if (newGender === null) { // not doing anything will make the picker "bounce back" } else { setIsCustomGender(false); let isValidGender: boolean = true; setForm({ ...form, gender: newGender, isValidGender, }); } }; const handleCustomGenderUpdate = (customGenderText: string) => { let isValidGender: boolean = genderRegex.test(customGenderText); customGenderText = customGenderText.replace(' ', '-'); setForm({ ...form, customGenderText, isValidGender, }); }; const handleSnapchatUpdate = (newUsername: string) => { // Allow any username, empty means to "un-link" it // TODO: refresh taggs bar after setForm({ ...form, snapchat: newUsername, }); }; const handleTikTokUpdate = (newUsername: string) => { // Allow any username, empty means to "un-link" it // TODO: refresh taggs bar after setForm({ ...form, tiktok: newUsername, }); }; const handleClassYearUpdate = (value: string) => { const classYear = parseInt(value, 10); setForm({ ...form, classYear, }); }; const handleSubmit = useCallback(async () => { if (!form.largePic) { Alert.alert(ERROR_UPLOAD_LARGE_PROFILE_PIC); return; } if (!form.smallPic) { Alert.alert(ERROR_UPLOAD_SMALL_PROFILE_PIC); return; } if (!form.attemptedSubmit) { setForm({ ...form, attemptedSubmit: true, }); } let invalidFields: boolean = false; const request = new FormData(); if (form.largePic) { request.append('largeProfilePicture', { uri: form.largePic, name: 'large_profile_pic.jpg', type: 'image/jpg', }); } if (form.smallPic) { request.append('smallProfilePicture', { uri: form.smallPic, name: 'small_profile_pic.jpg', type: 'image/jpg', }); } if (form.isValidWebsite) { request.append('website', form.website); } else { setForm({...form, attemptedSubmit: false}); setTimeout(() => setForm({...form, attemptedSubmit: true})); invalidFields = true; } if (form.bio) { if (form.isValidBio) { request.append('biography', form.bio.trim()); } else { setForm({...form, attemptedSubmit: false}); setTimeout(() => setForm({...form, attemptedSubmit: true})); invalidFields = true; } } if (isCustomGender) { if (form.isValidGender) { request.append('gender', form.customGenderText); } else { setForm({...form, attemptedSubmit: false}); setTimeout(() => setForm({...form, attemptedSubmit: true})); invalidFields = true; } } else { if (form.isValidGender) { request.append('gender', form.gender); } } if (form.isValidSnapchat) { request.append('snapchat', form.snapchat); } else { setForm({...form, attemptedSubmit: false}); setTimeout(() => setForm({...form, attemptedSubmit: true})); invalidFields = true; } if (form.isValidTiktok) { request.append('tiktok', form.tiktok); } else { setForm({...form, attemptedSubmit: false}); setTimeout(() => setForm({...form, attemptedSubmit: true})); invalidFields = true; } if (form.classYear !== university_class) { if (!form.classYear) { invalidFields = true; Alert.alert('Please select a valid class year'); } else { request.append('university_class', form.classYear); } } if (invalidFields) { return; } patchEditProfile(request, userId) .then((_) => { setNeedsUpdate(true); navigation.pop(); }) .catch((error) => { Alert.alert(error); }); }, [form, isCustomGender, university_class, userId, navigation]); React.useLayoutEffect(() => { navigation.setOptions({ headerRight: () => (