aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/assets/icons/grey-purple-plus.svg5
-rw-r--r--src/assets/icons/purple-plus.svg15
-rw-r--r--src/components/profile/Cover.tsx196
-rw-r--r--src/components/profile/TaggAvatar.tsx155
-rw-r--r--src/screens/profile/EditProfile.tsx6
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',