diff options
Diffstat (limited to 'src/components')
-rw-r--r-- | src/components/comments/AddComment.tsx | 103 | ||||
-rw-r--r-- | src/components/comments/CommentTile.tsx | 71 | ||||
-rw-r--r-- | src/components/comments/CommentsCount.tsx | 57 | ||||
-rw-r--r-- | src/components/comments/index.ts | 3 | ||||
-rw-r--r-- | src/components/index.ts | 2 | ||||
-rw-r--r-- | src/components/moments/CaptionScreenHeader.tsx (renamed from src/components/profile/CaptionScreenHeader.tsx) | 0 | ||||
-rw-r--r-- | src/components/moments/Moment.tsx (renamed from src/components/profile/Moment.tsx) | 0 | ||||
-rw-r--r-- | src/components/moments/MomentTile.tsx (renamed from src/components/profile/MomentTile.tsx) | 0 | ||||
-rw-r--r-- | src/components/moments/index.ts | 2 | ||||
-rw-r--r-- | src/components/profile/Content.tsx | 5 | ||||
-rw-r--r-- | src/components/profile/ProfilePreview.tsx (renamed from src/components/search/SearchResult.tsx) | 120 | ||||
-rw-r--r-- | src/components/profile/index.ts | 3 | ||||
-rw-r--r-- | src/components/search/SearchResults.tsx | 5 |
13 files changed, 326 insertions, 45 deletions
diff --git a/src/components/comments/AddComment.tsx b/src/components/comments/AddComment.tsx new file mode 100644 index 00000000..65c0b066 --- /dev/null +++ b/src/components/comments/AddComment.tsx @@ -0,0 +1,103 @@ +import * as React from 'react'; +import {Image, StyleSheet, TextInput, View} from 'react-native'; +import AsyncStorage from '@react-native-community/async-storage'; +import {AuthContext} from '../../routes'; +import {TaggBigInput} from '../onboarding'; +import {postMomentComment} from '../../services'; + +/** + * This file provides the add comment view for a user. + * Displays the logged in user's profile picture to the left and then provides space to add a comment. + * Comment is posted when enter is pressed as requested by product team. + */ + +export interface AddCommentProps { + setNewCommentsAvailable: Function; + moment_id: string; +} + +const AddComment: React.FC<AddCommentProps> = ({ + setNewCommentsAvailable, + moment_id, +}) => { + const [comment, setComment] = React.useState(''); + const { + avatar, + user: {userId, username}, + logout, + } = React.useContext(AuthContext); + + const handleCommentUpdate = (comment: string) => { + setComment(comment); + }; + + const postComment = async () => { + try { + const token = await AsyncStorage.getItem('token'); + if (!token) { + logout(); + return; + } + const postedComment = await postMomentComment( + userId, + comment, + moment_id, + token, + ); + + if (postedComment) { + //Set the current comment to en empty string if the comment was posted successfully. + handleCommentUpdate(''); + + //Indicate the MomentCommentsScreen that it needs to download the new comments again + setNewCommentsAvailable(true); + } + } catch (err) { + console.log('Error while posting comment!'); + } + }; + + return ( + <View style={styles.container}> + <Image + style={styles.avatar} + source={ + avatar + ? {uri: avatar} + : require('../../assets/images/avatar-placeholder.png') + } + /> + <TaggBigInput + style={styles.text} + multiline + placeholder="Add a comment....." + placeholderTextColor="gray" + onChangeText={handleCommentUpdate} + onSubmitEditing={postComment} + value={comment} + /> + </View> + ); +}; +const styles = StyleSheet.create({ + container: {flexDirection: 'row'}, + text: { + position: 'relative', + right: '18%', + backgroundColor: 'white', + width: '70%', + paddingLeft: '2%', + paddingRight: '2%', + paddingBottom: '1%', + paddingTop: '1%', + height: 60, + }, + avatar: { + height: 40, + width: 40, + borderRadius: 30, + marginRight: 15, + }, +}); + +export default AddComment; diff --git a/src/components/comments/CommentTile.tsx b/src/components/comments/CommentTile.tsx new file mode 100644 index 00000000..02840d47 --- /dev/null +++ b/src/components/comments/CommentTile.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import {Text, View} from 'react-native-animatable'; +import {ProfilePreview} from '../profile'; +import {CommentType} from '../../types'; +import {StyleSheet} from 'react-native'; +import {getTimePosted} from '../../utils'; +import ClockIcon from '../../assets/icons/clock-icon-01.svg'; + +/** + * Displays users's profile picture, comment posted by them and the time difference between now and when a comment was posted. + */ + +interface CommentTileProps { + comment_object: CommentType; +} + +const CommentTile: React.FC<CommentTileProps> = ({comment_object}) => { + const timePosted = getTimePosted(comment_object.date_time); + return ( + <View style={styles.container}> + <ProfilePreview + profilePreview={{ + id: comment_object.commenter__id, + username: comment_object.commenter__username, + first_name: '', + last_name: '', + }} + isComment={true} + /> + <View style={styles.body}> + <Text style={styles.comment}>{comment_object.comment}</Text> + <View style={styles.clockIconAndTime}> + <ClockIcon style={styles.clockIcon} /> + <Text style={styles.date_time}>{' ' + timePosted}</Text> + </View> + </View> + </View> + ); +}; + +const styles = StyleSheet.create({ + container: { + marginLeft: '3%', + marginRight: '3%', + borderBottomWidth: 1, + borderColor: 'lightgray', + marginBottom: '3%', + }, + body: { + marginLeft: 56, + }, + comment: { + position: 'relative', + top: -5, + marginBottom: '2%', + }, + date_time: { + color: 'gray', + }, + clockIcon: { + width: 12, + height: 12, + alignSelf: 'center', + }, + clockIconAndTime: { + flexDirection: 'row', + marginBottom: '3%', + }, +}); + +export default CommentTile; diff --git a/src/components/comments/CommentsCount.tsx b/src/components/comments/CommentsCount.tsx new file mode 100644 index 00000000..74b4194c --- /dev/null +++ b/src/components/comments/CommentsCount.tsx @@ -0,0 +1,57 @@ +import * as React from 'react'; +import {Text} from 'react-native-animatable'; +import {StyleSheet, TouchableOpacity} from 'react-native'; +import CommentIcon from '../../assets/icons/moment-comment-icon.svg'; +import {useNavigation} from '@react-navigation/native'; + +/** + * Provides a view for the comment icon and the comment count. + * When the user clicks on this view, a new screen opens to display all the comments. + */ + +type CommentsCountProps = { + comments_count: string; + isProfileView: boolean; + moment_id: string; +}; + +const CommentsCount: React.FC<CommentsCountProps> = ({ + comments_count, + isProfileView, + moment_id, +}) => { + const navigation = useNavigation(); + const navigateToCommentsScreen = async () => { + navigation.navigate('MomentCommentsScreen', { + isProfileView: isProfileView, + moment_id: moment_id, + }); + }; + return ( + <> + <TouchableOpacity onPress={() => navigateToCommentsScreen()}> + <CommentIcon style={styles.image} /> + <Text style={styles.count}> + {comments_count !== '0' ? comments_count : ''} + </Text> + </TouchableOpacity> + </> + ); +}; + +const styles = StyleSheet.create({ + image: { + position: 'relative', + width: 21, + height: 21, + }, + + count: { + position: 'relative', + fontWeight: 'bold', + color: 'white', + paddingTop: '2%', + }, +}); + +export default CommentsCount; diff --git a/src/components/comments/index.ts b/src/components/comments/index.ts new file mode 100644 index 00000000..6293f799 --- /dev/null +++ b/src/components/comments/index.ts @@ -0,0 +1,3 @@ +export {default as CommentsCount} from '../comments/CommentsCount'; +export {default as CommentTile} from './CommentTile'; +export {default as AddComment} from './AddComment'; diff --git a/src/components/index.ts b/src/components/index.ts index 1b726051..46a7773f 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -3,3 +3,5 @@ export * from './onboarding'; export * from './profile'; export * from './search'; export * from './taggs'; +export * from './comments'; +export * from './moments'; diff --git a/src/components/profile/CaptionScreenHeader.tsx b/src/components/moments/CaptionScreenHeader.tsx index 4715b4ef..4715b4ef 100644 --- a/src/components/profile/CaptionScreenHeader.tsx +++ b/src/components/moments/CaptionScreenHeader.tsx diff --git a/src/components/profile/Moment.tsx b/src/components/moments/Moment.tsx index 1ec5511e..1ec5511e 100644 --- a/src/components/profile/Moment.tsx +++ b/src/components/moments/Moment.tsx diff --git a/src/components/profile/MomentTile.tsx b/src/components/moments/MomentTile.tsx index 70b20d40..70b20d40 100644 --- a/src/components/profile/MomentTile.tsx +++ b/src/components/moments/MomentTile.tsx diff --git a/src/components/moments/index.ts b/src/components/moments/index.ts new file mode 100644 index 00000000..339e0e19 --- /dev/null +++ b/src/components/moments/index.ts @@ -0,0 +1,2 @@ +export {default as CaptionScreenHeader} from '../moments/CaptionScreenHeader'; +export {default as Moment} from './Moment'; 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/search/SearchResult.tsx b/src/components/profile/ProfilePreview.tsx index 04624004..c527746a 100644 --- a/src/components/search/SearchResult.tsx +++ b/src/components/profile/ProfilePreview.tsx @@ -19,11 +19,23 @@ const NO_USER: UserType = { username: '', }; -interface SearchResultProps extends ViewProps { +/** + * 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 SearchResult: React.FC<SearchResultProps> = ({ +const ProfilePreview: React.FC<ProfilePreviewProps> = ({ profilePreview: {username, first_name, last_name, id}, + isComment, style, }) => { const navigation = useNavigation(); @@ -78,58 +90,75 @@ const SearchResult: React.FC<SearchResultProps> = ({ last_name, }; try { - 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(); + 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); } - recentlySearchedList.unshift(user); } + } else { + recentlySearchedList = [user]; + } + + try { + let recentlySearchedListString = JSON.stringify(recentlySearchedList); + await AsyncStorage.setItem( + '@recently_searched_users', + recentlySearchedListString, + ); + } catch (e) { + console.log(e); } - } else { - recentlySearchedList = [user]; } //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. - //Not sure if we should make this call before caching the search results ?? loadProfile(user.id, user.username); updateMoments(true); - navigation.navigate('Profile', { - isProfileView: true, - }); - - try { - let recentlySearchedListString = JSON.stringify(recentlySearchedList); - await AsyncStorage.setItem( - '@recently_searched_users', - recentlySearchedListString, - ); - } catch (e) { - console.log(e); + 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={styles.avatar} + style={avatarStyle} source={ avatarURI ? {uri: avatarURI} @@ -137,8 +166,12 @@ const SearchResult: React.FC<SearchResultProps> = ({ } /> <View style={styles.nameContainer}> - <Text style={styles.username}>@{username}</Text> - <Text style={styles.name}>{first_name.concat(' ', last_name)}</Text> + <Text style={usernameStyle}>{usernameToDisplay}</Text> + {first_name ? ( + <Text style={styles.name}>{first_name.concat(' ', last_name)}</Text> + ) : ( + React.Fragment + )} </View> </TouchableOpacity> ); @@ -149,24 +182,35 @@ const styles = StyleSheet.create({ flexDirection: 'row', alignItems: 'center', }, - avatar: { + 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', }, - username: { + searchUsername: { fontSize: 18, fontWeight: '500', }, + commentUsername: { + fontSize: 16, + fontWeight: '500', + }, name: { fontSize: 16, color: '#333', }, }); -export default SearchResult; +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'; diff --git a/src/components/search/SearchResults.tsx b/src/components/search/SearchResults.tsx index 16bff818..57db4167 100644 --- a/src/components/search/SearchResults.tsx +++ b/src/components/search/SearchResults.tsx @@ -1,6 +1,6 @@ import React from 'react'; import {ProfilePreviewType} from '../../types'; -import SearchResult from './SearchResult'; +import ProfilePreview from '../profile/ProfilePreview'; import {StyleSheet, View} from 'react-native'; interface SearchResultsProps { results: Array<ProfilePreviewType>; @@ -9,10 +9,11 @@ const SearchResults: React.FC<SearchResultsProps> = ({results}) => { return ( <View> {results.map((profilePreview) => ( - <SearchResult + <ProfilePreview style={styles.result} key={profilePreview.id} {...{profilePreview}} + isComment={false} /> ))} </View> |