From d0237cbb61e5c4d77c7b0cefc50891639646ee91 Mon Sep 17 00:00:00 2001 From: Ashm Walia <40498934+ashmgarv@users.noreply.github.com> Date: Thu, 22 Oct 2020 15:34:21 -0700 Subject: [TMA 236] Comments PR (#64) * Added comments count and retrieve comments * Working draft * The one before cleanup * Finally * Added time icon and major refactoring * Small fix for social media taggs * Addressed review comments --- src/components/profile/CaptionScreenHeader.tsx | 37 ----- src/components/profile/Content.tsx | 5 +- src/components/profile/Moment.tsx | 126 --------------- src/components/profile/MomentTile.tsx | 33 ---- src/components/profile/ProfilePreview.tsx | 216 +++++++++++++++++++++++++ src/components/profile/index.ts | 3 +- 6 files changed, 219 insertions(+), 201 deletions(-) delete mode 100644 src/components/profile/CaptionScreenHeader.tsx delete mode 100644 src/components/profile/Moment.tsx delete mode 100644 src/components/profile/MomentTile.tsx create mode 100644 src/components/profile/ProfilePreview.tsx (limited to 'src/components/profile') 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 = ({ - title, - style, -}) => { - return ( - - - {title} - - - ); -}; - -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 = ({y, isProfileView}) => { }); setImagesMap(map); - console.log(map); }, [imagesList]); useEffect(() => { @@ -56,7 +55,7 @@ const Content: React.FC = ({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 = ({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 ( - - - {title} - {!isProfileView ? ( - navigateToImagePicker()} - /> - ) : ( - - )} - - - {images && - images.map((imageObj: MomentType) => ( - - ))} - {(images === undefined || images.length === 0) && !isProfileView && ( - navigateToImagePicker()}> - - - - - Add a moment of your {title.toLowerCase()}! - - - - - )} - - - ); -}; - -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 = ({moment}) => { - const navigation = useNavigation(); - const {path_hash} = moment; - return ( - { - navigation.navigate('IndividualMoment', {moment}); - }}> - - - - - ); -}; - -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 = ({ + profilePreview: {username, first_name, last_name, id}, + isComment, + style, +}) => { + const navigation = useNavigation(); + const {loadProfile, updateMoments} = useContext(ProfileContext); + const [avatarURI, setAvatarURI] = useState(null); + const [user, setUser] = useState(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 ( + + + + {usernameToDisplay} + {first_name ? ( + {first_name.concat(' ', last_name)} + ) : ( + React.Fragment + )} + + + ); +}; + +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'; -- cgit v1.2.3-70-g09d2