diff options
Diffstat (limited to 'src/components/profile')
-rw-r--r-- | src/components/profile/CaptionScreenHeader.tsx | 37 | ||||
-rw-r--r-- | src/components/profile/Content.tsx | 5 | ||||
-rw-r--r-- | src/components/profile/Moment.tsx | 126 | ||||
-rw-r--r-- | src/components/profile/MomentTile.tsx | 33 | ||||
-rw-r--r-- | src/components/profile/ProfilePreview.tsx | 216 | ||||
-rw-r--r-- | src/components/profile/index.ts | 3 |
6 files changed, 219 insertions, 201 deletions
diff --git a/src/components/profile/CaptionScreenHeader.tsx b/src/components/profile/CaptionScreenHeader.tsx deleted file mode 100644 index 4715b4ef..00000000 --- a/src/components/profile/CaptionScreenHeader.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; -import {Text, View, StyleSheet, ViewProps} from 'react-native'; -interface CaptionScreenHeaderProps extends ViewProps { - title: string; -} -const CaptionScreenHeader: React.FC<CaptionScreenHeaderProps> = ({ - title, - style, -}) => { - return ( - <View style={[styles.container, style]}> - <View style={styles.headerContainer}> - <Text style={styles.header}>{title}</Text> - </View> - </View> - ); -}; - -const styles = StyleSheet.create({ - container: { - flexDirection: 'row', - justifyContent: 'center', - height: 30, - }, - headerContainer: { - position: 'absolute', - left: '50%', - }, - header: { - position: 'relative', - right: '50%', - fontSize: 20, - fontWeight: 'bold', - color: 'white', - }, -}); -export default CaptionScreenHeader; diff --git a/src/components/profile/Content.tsx b/src/components/profile/Content.tsx index 0bf66dc7..8f20cd8d 100644 --- a/src/components/profile/Content.tsx +++ b/src/components/profile/Content.tsx @@ -7,7 +7,7 @@ import {MomentType} from 'src/types'; import {defaultMoments, MOMENTS_ENDPOINT} from '../../constants'; import {SCREEN_HEIGHT} from '../../utils'; import TaggsBar from '../taggs/TaggsBar'; -import Moment from './Moment'; +import {Moment} from '../moments'; import ProfileBody from './ProfileBody'; import ProfileCutout from './ProfileCutout'; import ProfileHeader from './ProfileHeader'; @@ -45,7 +45,6 @@ const Content: React.FC<ContentProps> = ({y, isProfileView}) => { }); setImagesMap(map); - console.log(map); }, [imagesList]); useEffect(() => { @@ -56,7 +55,7 @@ const Content: React.FC<ContentProps> = ({y, isProfileView}) => { const retrieveMoments = async () => { try { const token = await AsyncStorage.getItem('token'); - const response = await fetch(MOMENTS_ENDPOINT + `${userId}/`, { + const response = await fetch(MOMENTS_ENDPOINT + '?user_id=' + userId, { method: 'GET', headers: { Authorization: 'Token ' + token, diff --git a/src/components/profile/Moment.tsx b/src/components/profile/Moment.tsx deleted file mode 100644 index 1ec5511e..00000000 --- a/src/components/profile/Moment.tsx +++ /dev/null @@ -1,126 +0,0 @@ -import {useNavigation} from '@react-navigation/native'; -import React from 'react'; -import {Alert, StyleSheet, View} from 'react-native'; -import {Text} from 'react-native-animatable'; -import {ScrollView, TouchableOpacity} from 'react-native-gesture-handler'; -import LinearGradient from 'react-native-linear-gradient'; -import PlusIcon from '../../assets/icons/plus_icon-01.svg'; -import BigPlusIcon from '../../assets/icons/plus_icon-02.svg'; -import {MOMENTS_TITLE_COLOR} from '../../constants'; -import {SCREEN_WIDTH} from '../../utils'; -import ImagePicker from 'react-native-image-crop-picker'; -import MomentTile from './MomentTile'; -import {MomentType} from 'src/types'; - -interface MomentProps { - title: string; - images: MomentType[] | undefined; - isProfileView: boolean; -} - -const Moment: React.FC<MomentProps> = ({title, images, isProfileView}) => { - const navigation = useNavigation(); - - const navigateToImagePicker = () => { - ImagePicker.openPicker({ - width: 580, - height: 580, - cropping: true, - cropperToolbarTitle: 'Upload a moment', - mediaType: 'photo', - }) - .then((picture) => { - if ('path' in picture) { - navigation.navigate('CaptionScreen', { - title: title, - image: picture, - }); - } - }) - .catch((err) => { - Alert.alert('Unable to upload moment!'); - }); - }; - return ( - <View style={styles.container}> - <View style={styles.header}> - <Text style={styles.titleText}>{title}</Text> - {!isProfileView ? ( - <PlusIcon - width={21} - height={21} - onPress={() => navigateToImagePicker()} - /> - ) : ( - <React.Fragment /> - )} - </View> - <ScrollView - horizontal - showsHorizontalScrollIndicator={false} - style={styles.scrollContainer}> - {images && - images.map((imageObj: MomentType) => ( - <MomentTile key={imageObj.moment_id} moment={imageObj} /> - ))} - {(images === undefined || images.length === 0) && !isProfileView && ( - <TouchableOpacity onPress={() => navigateToImagePicker()}> - <LinearGradient - colors={['rgba(105, 141, 211, 1)', 'rgba(105, 141, 211, 0.3)']}> - <View style={styles.defaultImage}> - <BigPlusIcon width={50} height={50} /> - <Text style={styles.defaultImageText}> - Add a moment of your {title.toLowerCase()}! - </Text> - </View> - </LinearGradient> - </TouchableOpacity> - )} - </ScrollView> - </View> - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - flexDirection: 'column', - backgroundColor: '#eee', - }, - header: { - flex: 1, - paddingHorizontal: 10, - padding: 5, - paddingTop: 20, - backgroundColor: 'white', - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - }, - titleText: { - fontSize: 16, - fontWeight: 'bold', - color: MOMENTS_TITLE_COLOR, - }, - scrollContainer: { - height: SCREEN_WIDTH / 2, - backgroundColor: '#eee', - }, - defaultImage: { - aspectRatio: 1, - height: '100%', - alignItems: 'center', - justifyContent: 'center', - flexDirection: 'column', - }, - defaultImageText: { - fontSize: 20, - paddingTop: 20, - color: 'white', - fontWeight: 'bold', - width: '75%', - textAlign: 'center', - }, -}); - -export default Moment; diff --git a/src/components/profile/MomentTile.tsx b/src/components/profile/MomentTile.tsx deleted file mode 100644 index 70b20d40..00000000 --- a/src/components/profile/MomentTile.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import {useNavigation} from '@react-navigation/native'; -import React from 'react'; -import {StyleSheet, View, Image, TouchableOpacity} from 'react-native'; -import {MomentType} from 'src/types'; - -interface MomentTileProps { - moment: MomentType; -} -const MomentTile: React.FC<MomentTileProps> = ({moment}) => { - const navigation = useNavigation(); - const {path_hash} = moment; - return ( - <TouchableOpacity - onPress={() => { - navigation.navigate('IndividualMoment', {moment}); - }}> - <View style={styles.image}> - <Image style={styles.image} source={{uri: path_hash}} /> - </View> - </TouchableOpacity> - ); -}; - -const styles = StyleSheet.create({ - image: { - aspectRatio: 1, - height: '100%', - alignItems: 'center', - justifyContent: 'center', - flexDirection: 'column', - }, -}); -export default MomentTile; diff --git a/src/components/profile/ProfilePreview.tsx b/src/components/profile/ProfilePreview.tsx new file mode 100644 index 00000000..c527746a --- /dev/null +++ b/src/components/profile/ProfilePreview.tsx @@ -0,0 +1,216 @@ +import React, {useEffect, useState, useContext} from 'react'; +import {ProfilePreviewType} from '../../types'; +import { + View, + Text, + Image, + StyleSheet, + ViewProps, + TouchableOpacity, +} from 'react-native'; +import {useNavigation} from '@react-navigation/native'; +import RNFetchBlob from 'rn-fetch-blob'; +import AsyncStorage from '@react-native-community/async-storage'; +import {AVATAR_PHOTO_ENDPOINT} from '../../constants'; +import {UserType} from '../../types'; +import {ProfileContext} from '../../routes/viewProfile'; +const NO_USER: UserType = { + userId: '', + username: '', +}; + +/** + * This component returns user's profile picture followed by username as a touchable component. + * What happens when someone clicks on this component is partly decided by the prop isComment. + * If isComment is true then it means that we are not displaying this tile as a part of search results. + * And hence we do not cache the search results. + * On the other hand, if isComment is false, then we should update the search cache. (This cache needs to be revamped to clear outdated results.) + * In either case, we load the ProfileContext with data and set the getNewMoments flag to true (Which ensures that everything that needs to be displayed on a user's profile is set). + * Finally, We navigate to Profile if we are on the Search Stack. Else we navigate to ProfileView. + */ + +interface ProfilePreviewProps extends ViewProps { + profilePreview: ProfilePreviewType; + isComment: boolean; +} +const ProfilePreview: React.FC<ProfilePreviewProps> = ({ + profilePreview: {username, first_name, last_name, id}, + isComment, + style, +}) => { + const navigation = useNavigation(); + const {loadProfile, updateMoments} = useContext(ProfileContext); + const [avatarURI, setAvatarURI] = useState<string | null>(null); + const [user, setUser] = useState<UserType>(NO_USER); + useEffect(() => { + let mounted = true; + const loadAvatar = async () => { + try { + const token = await AsyncStorage.getItem('token'); + if (!token) { + setUser(NO_USER); + return; + } + const response = await RNFetchBlob.config({ + fileCache: true, + appendExt: 'jpg', + }).fetch('GET', AVATAR_PHOTO_ENDPOINT + `${id}/`, { + Authorization: 'Token ' + token, + }); + const status = response.info().status; + if (status === 200) { + if (mounted) { + setAvatarURI(response.path()); + } + return; + } + if (mounted) { + setAvatarURI(''); + } + } catch (error) { + console.log(error); + } + }; + loadAvatar(); + return () => { + mounted = false; + }; + }, [id]); + + /** + * Adds a searched user to the recently searched cache if they're tapped on. + * Cache maintains 10 recently searched users, popping off the oldest one if + * needed to make space. + */ + const addToRecentlyStoredAndNavigateToProfile = async () => { + let user: ProfilePreviewType = { + id, + username, + first_name, + last_name, + }; + try { + if (!isComment) { + const jsonValue = await AsyncStorage.getItem( + '@recently_searched_users', + ); + let recentlySearchedList = + jsonValue != null ? JSON.parse(jsonValue) : null; + if (recentlySearchedList) { + if (recentlySearchedList.length > 0) { + if ( + recentlySearchedList.some( + (saved_user: ProfilePreviewType) => saved_user.id === id, + ) + ) { + console.log('User already in recently searched.'); + } else { + if (recentlySearchedList.length >= 10) { + recentlySearchedList.pop(); + } + recentlySearchedList.unshift(user); + } + } + } else { + recentlySearchedList = [user]; + } + + try { + let recentlySearchedListString = JSON.stringify(recentlySearchedList); + await AsyncStorage.setItem( + '@recently_searched_users', + recentlySearchedListString, + ); + } catch (e) { + console.log(e); + } + } + + //Load user profile and set new moments to true, navigate to Profile + //Load user profile makes sure that we actually load profile of the user the logged in user want to view + //Set new moments to true makes sure that we download the moment for the user being viewed again. + loadProfile(user.id, user.username); + updateMoments(true); + if (!isComment) { + navigation.navigate('Profile', { + isProfileView: true, + }); + } else { + navigation.navigate('ProfileView', { + isProfileView: true, + }); + } + } catch (e) { + console.log(e); + } + }; + + //With @ sign if on search screen. + const usernameToDisplay = !isComment ? `@` + username : username; + const usernameStyle = isComment + ? styles.commentUsername + : styles.searchUsername; + + const avatarStyle = !isComment ? styles.searchAvatar : styles.commentAvatar; + + return ( + <TouchableOpacity + onPress={addToRecentlyStoredAndNavigateToProfile} + style={[styles.container, style]}> + <Image + style={avatarStyle} + source={ + avatarURI + ? {uri: avatarURI} + : require('../../assets/images/avatar-placeholder.png') + } + /> + <View style={styles.nameContainer}> + <Text style={usernameStyle}>{usernameToDisplay}</Text> + {first_name ? ( + <Text style={styles.name}>{first_name.concat(' ', last_name)}</Text> + ) : ( + React.Fragment + )} + </View> + </TouchableOpacity> + ); +}; + +const styles = StyleSheet.create({ + container: { + flexDirection: 'row', + alignItems: 'center', + }, + searchAvatar: { + height: 60, + width: 60, + borderRadius: 30, + marginRight: 15, + }, + commentAvatar: { + height: 40, + width: 40, + borderRadius: 20, + marginRight: 15, + marginTop: '2%', + }, + nameContainer: { + justifyContent: 'space-evenly', + alignSelf: 'stretch', + }, + searchUsername: { + fontSize: 18, + fontWeight: '500', + }, + commentUsername: { + fontSize: 16, + fontWeight: '500', + }, + name: { + fontSize: 16, + color: '#333', + }, +}); + +export default ProfilePreview; diff --git a/src/components/profile/index.ts b/src/components/profile/index.ts index e2063e26..eb65d509 100644 --- a/src/components/profile/index.ts +++ b/src/components/profile/index.ts @@ -1,7 +1,6 @@ export {default as Cover} from './Cover'; export {default as Content} from './Content'; -export {default as Moment} from './Moment'; export {default as ProfileCutout} from './ProfileCutout'; export {default as ProfileBody} from './ProfileBody'; export {default as ProfileHeader} from './ProfileHeader'; -export {default as CaptionScreenHeader} from './CaptionScreenHeader'; +export {default as ProfilePreview} from '../profile/ProfilePreview'; |