diff options
author | Brian Kim <brian@tagg.id> | 2021-05-12 11:42:14 -0700 |
---|---|---|
committer | Brian Kim <brian@tagg.id> | 2021-05-12 11:42:14 -0700 |
commit | 0e1f57be13b282b516f1bf690f2890b9eabf3188 (patch) | |
tree | 32db1d0e8b85986c9bad8e8f5162ce76bb221557 | |
parent | 64971e5d67878ee5bd430dfa89dfe12ac532f5e6 (diff) |
Plus sign for profile and header in profile, ability to add on the screen, reloads screen, saved to database
-rw-r--r-- | src/assets/icons/grey-purple-plus.svg | 5 | ||||
-rw-r--r-- | src/assets/icons/purple-plus.svg | 15 | ||||
-rw-r--r-- | src/components/profile/Cover.tsx | 196 | ||||
-rw-r--r-- | src/components/profile/TaggAvatar.tsx | 155 | ||||
-rw-r--r-- | src/screens/profile/EditProfile.tsx | 6 |
5 files changed, 357 insertions, 20 deletions
diff --git a/src/assets/icons/grey-purple-plus.svg b/src/assets/icons/grey-purple-plus.svg new file mode 100644 index 00000000..2053d4a7 --- /dev/null +++ b/src/assets/icons/grey-purple-plus.svg @@ -0,0 +1,5 @@ +<svg width="31" height="31" viewBox="0 0 31 31" fill="none" xmlns="http://www.w3.org/2000/svg"> +<circle cx="15.5" cy="15.5" r="15.5" fill="white"/> +<rect width="2.38462" height="16.6923" rx="1.19231" transform="matrix(-1 0 0 1 16.6934 7.15381)" fill="#8F00FF"/> +<rect width="2.38462" height="16.6923" rx="1.19231" transform="matrix(0.00550217 0.999985 0.999985 -0.00550217 7.1543 14.4004)" fill="#8F00FF"/> +</svg> diff --git a/src/assets/icons/purple-plus.svg b/src/assets/icons/purple-plus.svg new file mode 100644 index 00000000..20949b6d --- /dev/null +++ b/src/assets/icons/purple-plus.svg @@ -0,0 +1,15 @@ +<svg width="23" height="23" viewBox="0 0 23 23" fill="none" xmlns="http://www.w3.org/2000/svg"> +<circle cx="11.5" cy="11.5" r="11.25" fill="url(#paint0_linear)" stroke="url(#paint1_linear)" stroke-width="0.5"/> +<rect width="1.76923" height="12.3846" rx="0.884615" transform="matrix(-1 0 0 1 12.3848 5.30762)" fill="white"/> +<rect width="1.76923" height="12.3846" rx="0.884615" transform="matrix(0.00550217 0.999985 0.999985 -0.00550217 5.30859 10.6841)" fill="white"/> +<defs> +<linearGradient id="paint0_linear" x1="11.5" y1="0" x2="10.9524" y2="30.119" gradientUnits="userSpaceOnUse"> +<stop stop-color="#8F01FF"/> +<stop offset="1" stop-color="#6EE7E7"/> +</linearGradient> +<linearGradient id="paint1_linear" x1="11.5" y1="0" x2="10.9524" y2="32.8571" gradientUnits="userSpaceOnUse"> +<stop stop-color="#8F01FF"/> +<stop offset="1" stop-color="#6EE7E7"/> +</linearGradient> +</defs> +</svg> diff --git a/src/components/profile/Cover.tsx b/src/components/profile/Cover.tsx index 27777b64..8aa6b0d3 100644 --- a/src/components/profile/Cover.tsx +++ b/src/components/profile/Cover.tsx @@ -1,28 +1,181 @@ -import React from 'react'; -import {Image, StyleSheet, View} from 'react-native'; -import {useSelector} from 'react-redux'; +import React, {useState, useEffect} from 'react'; +import { + Alert, + Image, + StyleSheet, + View, + TouchableOpacity, + Text, + ImageBackground, +} from 'react-native'; import {COVER_HEIGHT, IMAGE_WIDTH} from '../../constants'; -import {RootState} from '../../store/rootreducer'; import {ScreenType} from '../../types'; +import GreyPurplePlus from '../../assets/icons/grey-purple-plus.svg'; +import ImagePicker from 'react-native-image-crop-picker'; +import { + ERROR_UPLOAD_LARGE_PROFILE_PIC, + ERROR_UPLOAD_SMALL_PROFILE_PIC, +} from '../../constants/strings'; +import {patchEditProfile} from '../../services'; +import {useDispatch, useSelector} from 'react-redux'; +import {loadUserData, resetHeaderAndProfileImage} from '../../store/actions'; +import {RootState} from '../../store/rootreducer'; interface CoverProps { userXId: string | undefined; screenType: ScreenType; } const Cover: React.FC<CoverProps> = ({userXId, screenType}) => { - const {cover} = useSelector((state: RootState) => + const dispatch = useDispatch(); + const {cover, user} = useSelector((state: RootState) => userXId ? state.userX[screenType][userXId] : state.user, ); - return ( - <View style={[styles.container]}> - <Image - style={styles.image} - defaultSource={require('../../assets/images/cover-placeholder.png')} - source={{uri: cover, cache: 'reload'}} - /> - </View> - ); + const [needsUpdate, setNeedsUpdate] = useState(false); + const [loading, setLoading] = useState(false); + const [validImage, setValidImage] = useState<boolean>(true); + + const { + profile: {website, biography, gender, snapchat, tiktok, university_class}, + avatar, + } = useSelector((state: RootState) => state.user); + + const isCustomGender = + 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, + }); + + useEffect(() => { + checkAvatar(cover); + }, []); + + useEffect(() => { + if (needsUpdate) { + const userId = user.userId; + const username = user.username; + dispatch(resetHeaderAndProfileImage()); + dispatch(loadUserData({userId, username})); + } + }, [dispatch, needsUpdate]); + + 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, + }); + handleSubmit(); + } + }); + }; + + const handleSubmit = () => { + if (!form.largePic) { + Alert.alert(ERROR_UPLOAD_LARGE_PROFILE_PIC); + return; + } + if (!form.smallPic) { + Alert.alert(ERROR_UPLOAD_SMALL_PROFILE_PIC); + return; + } + + const request = new FormData(); + request.append('largeProfilePicture', { + uri: form.largePic, + name: 'large_profile_pic.jpg', + type: 'image/jpg', + }); + + setLoading(true); + patchEditProfile(request, user.userId) + .then((_) => { + setNeedsUpdate(true); + setValidImage(true); + // navigation.pop(); + }) + .catch((error) => { + Alert.alert(error); + }); + setLoading(false); + }; + + const checkAvatar = (url: string | undefined) => { + if (!url) { + setValidImage(false); + } + fetch(url) + .then((res) => { + if (res.status === 200) { + setValidImage(true); + } else { + setValidImage(false); + } + }) + .catch((err) => { + setValidImage(false); + }); + }; + + if (!validImage && userXId === undefined && !loading) { + return ( + <> + <View style={[styles.container]}> + <ImageBackground + style={styles.image} + defaultSource={require('../../assets/images/cover-placeholder.png')} + source={{uri: cover, cache: 'reload'}}> + <TouchableOpacity + accessible={true} + accessibilityLabel="ADD PROFILE PICTURE" + onPress={() => goToGalleryLargePic()}> + <GreyPurplePlus style={styles.plus} /> + <Text style={styles.text}>Add Picture</Text> + </TouchableOpacity> + </ImageBackground> + </View> + </> + ); + } else { + return ( + <View style={[styles.container]}> + <Image + style={styles.image} + defaultSource={require('../../assets/images/cover-placeholder.png')} + source={{uri: cover, cache: 'reload'}} + /> + </View> + ); + } }; const styles = StyleSheet.create({ @@ -33,5 +186,20 @@ const styles = StyleSheet.create({ width: IMAGE_WIDTH, height: COVER_HEIGHT, }, + plus: { + position: 'absolute', + top: 75, + right: 125, + }, + text: { + color: 'white', + position: 'absolute', + fontSize: 18, + top: 80, + right: 20, + }, + touch: { + flex: 1, + }, }); export default Cover; diff --git a/src/components/profile/TaggAvatar.tsx b/src/components/profile/TaggAvatar.tsx index ea0bdb65..33650a04 100644 --- a/src/components/profile/TaggAvatar.tsx +++ b/src/components/profile/TaggAvatar.tsx @@ -1,9 +1,17 @@ -import React from 'react'; -import {StyleSheet} from 'react-native'; -import {useSelector} from 'react-redux'; +import React, {useState, useEffect} from 'react'; +import {Alert, StyleSheet, TouchableOpacity} from 'react-native'; import {RootState} from '../../store/rootreducer'; import {ScreenType} from '../../types'; import {Avatar} from '../common'; +import {useDispatch, useSelector} from 'react-redux'; +import {loadUserData, resetHeaderAndProfileImage} from '../../store/actions'; +import PurplePlus from '../../assets/icons/purple-plus.svg'; +import ImagePicker from 'react-native-image-crop-picker'; +import { + ERROR_UPLOAD_LARGE_PROFILE_PIC, + ERROR_UPLOAD_SMALL_PROFILE_PIC, +} from '../../constants/strings'; +import {patchEditProfile} from '../../services'; const PROFILE_DIM = 100; @@ -20,8 +28,142 @@ const TaggAvatar: React.FC<TaggAvatarProps> = ({ const {avatar} = useSelector((state: RootState) => userXId ? state.userX[screenType][userXId] : state.user, ); + const dispatch = useDispatch(); + const [needsUpdate, setNeedsUpdate] = useState(false); + const [loading, setLoading] = useState(false); + const [validImage, setValidImage] = useState<boolean>(true); - return <Avatar style={[styles.image, style]} uri={avatar} />; + const { + profile: {website, biography, gender, snapchat, tiktok, university_class}, + cover, + } = useSelector((state: RootState) => state.user); + const {user} = useSelector((state: RootState) => + userXId ? state.userX[screenType][userXId] : state.user, + ); + const isCustomGender = + 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, + }); + + useEffect(() => { + checkAvatar(avatar); + }, []); + + useEffect(() => { + if (needsUpdate) { + const userId = user.userId; + const username = user.username; + dispatch(resetHeaderAndProfileImage()); + dispatch(loadUserData({userId, username})); + } + }, [dispatch, needsUpdate]); + + 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, + }); + handleSubmit(); + } + }); + }; + + const handleSubmit = async () => { + if (!form.largePic) { + Alert.alert(ERROR_UPLOAD_LARGE_PROFILE_PIC); + return; + } + if (!form.smallPic) { + Alert.alert(ERROR_UPLOAD_SMALL_PROFILE_PIC); + return; + } + + const request = new FormData(); + request.append('smallProfilePicture', { + uri: form.smallPic, + name: 'small_profile_pic.jpg', + type: 'image/jpg', + }); + + setLoading(true); + patchEditProfile(request, user.userId) + .then((_) => { + setNeedsUpdate(true); + setValidImage(true); + // navigation.pop(); + }) + .catch((error) => { + Alert.alert(error); + }); + setLoading(false); + }; + + const checkAvatar = (url: string | undefined) => { + if (!url) { + setValidImage(false); + } + fetch(url) + .then((res) => { + if (res.status === 200) { + setValidImage(true); + } else { + setValidImage(false); + } + }) + .catch((err) => { + setValidImage(false); + }); + }; + + console.log(avatar); + + if (!validImage && userXId === undefined && !loading) { + return ( + <> + <Avatar style={[styles.image, style]} uri={avatar} /> + <TouchableOpacity + accessible={true} + accessibilityLabel="ADD PROFILE PICTURE" + onPress={() => goToGallerySmallPic()}> + <PurplePlus style={[styles.plus]} /> + </TouchableOpacity> + </> + ); + } else { + return <Avatar style={[styles.image, style]} uri={avatar} />; + } }; const styles = StyleSheet.create({ @@ -30,6 +172,11 @@ const styles = StyleSheet.create({ width: PROFILE_DIM, borderRadius: PROFILE_DIM / 2, }, + plus: { + position: 'absolute', + bottom: 35, + right: 0, + }, }); export default TaggAvatar; diff --git a/src/screens/profile/EditProfile.tsx b/src/screens/profile/EditProfile.tsx index 26802e45..dfb8ba1f 100644 --- a/src/screens/profile/EditProfile.tsx +++ b/src/screens/profile/EditProfile.tsx @@ -61,7 +61,7 @@ interface EditProfileProps { const EditProfile: React.FC<EditProfileProps> = ({route, navigation}) => { const y: Animated.Value<number> = Animated.useValue(0); - const {userId, username} = route.params; + const {userId} = route.params; const { profile: {website, biography, gender, snapchat, tiktok, university_class}, avatar, @@ -74,10 +74,12 @@ const EditProfile: React.FC<EditProfileProps> = ({route, navigation}) => { useEffect(() => { if (needsUpdate) { + const userId = user.userId; + const username = user.username; dispatch(resetHeaderAndProfileImage()); dispatch(loadUserData({userId, username})); } - }, [dispatch, needsUpdate, userId, username]); + }, [dispatch, needsUpdate, user]); const [isCustomGender, setIsCustomGender] = React.useState<boolean>( gender !== '' && gender !== 'female' && gender !== 'male', |