From 9322aede82815ba5a6bddf5b289692955d6e1d96 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Fri, 30 Apr 2021 18:05:28 -0400 Subject: added display text parsing --- src/components/comments/CommentTile.tsx | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'src/components/comments/CommentTile.tsx') diff --git a/src/components/comments/CommentTile.tsx b/src/components/comments/CommentTile.tsx index 34eef418..334934f1 100644 --- a/src/components/comments/CommentTile.tsx +++ b/src/components/comments/CommentTile.tsx @@ -1,21 +1,22 @@ /* eslint-disable radix */ import React, {Fragment, useEffect, useRef, useState} from 'react'; -import {Text, View} from 'react-native-animatable'; -import {ProfilePreview} from '../profile'; -import {CommentType, ScreenType, TypeOfComment} from '../../types'; import {Alert, Animated, StyleSheet} from 'react-native'; -import ClockIcon from '../../assets/icons/clock-icon-01.svg'; -import {TAGG_LIGHT_BLUE} from '../../constants'; +import {Text, View} from 'react-native-animatable'; import {RectButton, TouchableOpacity} from 'react-native-gesture-handler'; -import {getTimePosted, normalize, SCREEN_WIDTH} from '../../utils'; +import Swipeable from 'react-native-gesture-handler/Swipeable'; +import {useSelector} from 'react-redux'; import Arrow from '../../assets/icons/back-arrow-colored.svg'; +import ClockIcon from '../../assets/icons/clock-icon-01.svg'; import Trash from '../../assets/ionicons/trash-outline.svg'; -import CommentsContainer from './CommentsContainer'; -import Swipeable from 'react-native-gesture-handler/Swipeable'; -import {deleteComment, getCommentsCount} from '../../services'; +import {TAGG_LIGHT_BLUE} from '../../constants'; import {ERROR_FAILED_TO_DELETE_COMMENT} from '../../constants/strings'; -import {useSelector} from 'react-redux'; +import {deleteComment, getCommentsCount} from '../../services'; import {RootState} from '../../store/rootReducer'; +import {CommentType, ScreenType, TypeOfComment} from '../../types'; +import {getTimePosted, normalize, SCREEN_WIDTH} from '../../utils'; +import {mentionPartTypes, renderValue} from '../../utils/comments'; +import {ProfilePreview} from '../profile'; +import CommentsContainer from './CommentsContainer'; /** * Displays users's profile picture, comment posted by them and the time difference between now and when a comment was posted. @@ -154,7 +155,9 @@ const CommentTile: React.FC = ({ screenType={screenType} /> - {comment_object.comment} + + {renderValue(comment_object.comment, mentionPartTypes)} + {' ' + timePosted} -- cgit v1.2.3-70-g09d2 From 2832decea88ed14325f92759617ae8ee8a588d22 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 4 May 2021 19:56:21 -0400 Subject: fixed bug, cleaned up code, mention working --- src/components/comments/AddComment.tsx | 62 +++++++------- src/components/comments/CommentTile.tsx | 103 ++++++++++------------- src/components/comments/CommentsContainer.tsx | 116 ++++++++++---------------- src/screens/profile/MomentCommentsScreen.tsx | 91 ++++++++++++-------- 4 files changed, 174 insertions(+), 198 deletions(-) (limited to 'src/components/comments/CommentTile.tsx') diff --git a/src/components/comments/AddComment.tsx b/src/components/comments/AddComment.tsx index cce846e5..dd016109 100644 --- a/src/components/comments/AddComment.tsx +++ b/src/components/comments/AddComment.tsx @@ -1,4 +1,4 @@ -import React, {useEffect, useRef, useState} from 'react'; +import React, {useContext, useEffect, useRef, useState} from 'react'; import { Keyboard, KeyboardAvoidingView, @@ -12,61 +12,59 @@ import {TouchableOpacity} from 'react-native-gesture-handler'; import {useDispatch, useSelector} from 'react-redux'; import UpArrowIcon from '../../assets/icons/up_arrow.svg'; import {TAGG_LIGHT_BLUE} from '../../constants'; +import {CommentContext} from '../../screens/profile/MomentCommentsScreen'; import {postComment} from '../../services'; import {updateReplyPosted} from '../../store/actions'; import {RootState} from '../../store/rootreducer'; -import {CommentType} from '../../types'; +import {CommentThreadType, CommentType} from '../../types'; import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; -import {Avatar} from '../common'; import {mentionPartTypes} from '../../utils/comments'; - -/** - * 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. - */ +import {Avatar} from '../common'; export interface AddCommentProps { - setNewCommentsAvailable: Function; - // we either have a comment object if we're replying to a comment, - // or a momentId if we're just commenting a moment. - commentInReplyTo: CommentType | undefined; - momentId: string | undefined; - isReplying: boolean; + momentId: string; placeholderText: string; } -const AddComment: React.FC = ({ - setNewCommentsAvailable, - commentInReplyTo, - momentId, - placeholderText, - isReplying, -}) => { +const AddComment: React.FC = ({momentId, placeholderText}) => { + const {setShouldUpdateAllComments, commentTapped} = useContext( + CommentContext, + ); const [inReplyToMention, setInReplyToMention] = useState(''); const [comment, setComment] = useState(''); const [keyboardVisible, setKeyboardVisible] = useState(false); const {avatar} = useSelector((state: RootState) => state.user); const dispatch = useDispatch(); const ref = useRef(null); - const objectId: string = isReplying - ? commentInReplyTo!.comment_id - : momentId!; + const isReplyingToComment = + commentTapped !== undefined && !('parent_comment' in commentTapped); + const isReplyingToReply = + commentTapped !== undefined && 'parent_comment' in commentTapped; + const objectId: string = commentTapped + ? 'parent_comment' in commentTapped + ? (commentTapped as CommentThreadType).parent_comment.comment_id + : (commentTapped as CommentType).comment_id + : momentId; const addComment = async () => { const trimmed = comment.trim(); if (trimmed === '') { return; } - const postedComment = await postComment(trimmed, objectId, isReplying); + const postedComment = await postComment( + inReplyToMention + trimmed, + objectId, + isReplyingToComment || isReplyingToReply, + ); if (postedComment) { setComment(''); + setInReplyToMention(''); //Set new reply posted object //This helps us show the latest reply on top //Data set is kind of stale but it works - if (isReplying) { + if (isReplyingToComment || isReplyingToReply) { dispatch( updateReplyPosted({ comment_id: postedComment.comment_id, @@ -74,7 +72,7 @@ const AddComment: React.FC = ({ }), ); } - setNewCommentsAvailable(true); + setShouldUpdateAllComments(true); } }; @@ -91,15 +89,17 @@ const AddComment: React.FC = ({ }, []); useEffect(() => { - if (isReplying && commentInReplyTo) { + if (isReplyingToComment || isReplyingToReply) { // bring up keyboard ref.current?.focus(); - const commenter = commentInReplyTo.commenter; + } + if (commentTapped && isReplyingToReply) { + const commenter = (commentTapped as CommentThreadType).commenter; setInReplyToMention(`@[${commenter.username}](${commenter.id}) `); } else { setInReplyToMention(''); } - }, [isReplying, commentInReplyTo]); + }, [isReplyingToComment, isReplyingToReply, commentTapped]); return ( void; - newCommentsAvailable: boolean; - setNewCommentsAvailable: (available: boolean) => void; + isThread: boolean; + shouldUpdateParent: boolean; + setShouldUpdateParent: (update: boolean) => void; canDelete: boolean; } const CommentTile: React.FC = ({ - comment_object, + commentObject, screenType, - typeOfComment, - setCommentObjectInFocus, - newCommentsAvailable, - setNewCommentsAvailable, + setShouldUpdateParent, + shouldUpdateParent, canDelete, + isThread, }) => { - const timePosted = getTimePosted(comment_object.date_created); + const {setCommentTapped} = useContext(CommentContext); + const timePosted = getTimePosted(commentObject.date_created); const [showReplies, setShowReplies] = useState(false); const [showKeyboard, setShowKeyboard] = useState(false); - const [newThreadAvailable, setNewThreadAvailable] = useState(true); + const [shouldUpdateChild, setShouldUpdateChild] = useState(true); const swipeRef = useRef(null); - const isThread = typeOfComment === 'Thread'; - const {replyPosted} = useSelector((state: RootState) => state.user); - /** - * Bubbling up, for handling a new comment in a thread. - */ useEffect(() => { - if (newCommentsAvailable) { - setNewThreadAvailable(true); + if (shouldUpdateParent) { + setShouldUpdateChild(true); } - }, [newCommentsAvailable]); + }, [shouldUpdateParent]); useEffect(() => { - if (replyPosted && typeOfComment === 'Comment') { - if (replyPosted.parent_comment.comment_id === comment_object.comment_id) { + if (replyPosted && !isThread) { + if (replyPosted.parent_comment.comment_id === commentObject.comment_id) { setShowReplies(true); } } - // eslint-disable-next-line react-hooks/exhaustive-deps }, [replyPosted]); - /** - * Case : A COMMENT IS IN FOCUS && REPLY SECTION IS HIDDEN - * Bring the current comment to focus - * Case : No COMMENT IS IN FOCUS && REPLY SECTION IS SHOWN - * Unfocus comment in focus - */ const toggleAddComment = () => { - //Do not allow user to reply to a thread - if (!isThread) { - if (setCommentObjectInFocus) { - if (!showKeyboard) { - setCommentObjectInFocus(comment_object); - } else { - setCommentObjectInFocus(undefined); - } - } - setShowKeyboard(!showKeyboard); - } + setCommentTapped(commentObject); + setShowKeyboard(!showKeyboard); }; const toggleReplies = async () => { - if (showReplies) { + if (showReplies && isThread) { + const comment = (commentObject as CommentThreadType).parent_comment; //To update count of replies in case we deleted a reply - comment_object.replies_count = parseInt( - await getCommentsCount(comment_object.comment_id, true), + comment.replies_count = parseInt( + await getCommentsCount(comment.comment_id, true), + 10, ); } - setNewThreadAvailable(true); + setShouldUpdateChild(true); setShowReplies(!showReplies); }; /** * Method to compute text to be shown for replies button */ - const getRepliesText = () => + const getRepliesText = (comment: CommentType) => showReplies ? 'Hide' - : comment_object.replies_count > 0 - ? `Replies (${comment_object.replies_count})` + : comment.replies_count > 0 + ? `Replies (${comment.replies_count})` : 'Replies'; const renderRightAction = (text: string, color: string) => { const pressHandler = async () => { swipeRef.current?.close(); - const success = await deleteComment(comment_object.comment_id, isThread); + const success = await deleteComment(commentObject.comment_id, isThread); if (success) { - setNewCommentsAvailable(true); + setShouldUpdateParent(true); } else { Alert.alert(ERROR_FAILED_TO_DELETE_COMMENT); } @@ -150,13 +130,13 @@ const CommentTile: React.FC = ({ - {renderValue(comment_object.comment, mentionPartTypes)} + {renderValue(commentObject.comment, mentionPartTypes)} @@ -165,11 +145,13 @@ const CommentTile: React.FC = ({ {/*** Show replies text only if there are some replies present */} - {typeOfComment === 'Comment' && comment_object.replies_count > 0 && ( + {!isThread && (commentObject as CommentType).replies_count > 0 && ( - {getRepliesText()} + + {getRepliesText(commentObject as CommentType)} + = ({ {showReplies && ( )} diff --git a/src/components/comments/CommentsContainer.tsx b/src/components/comments/CommentsContainer.tsx index 3dc8a71c..d5d02a92 100644 --- a/src/components/comments/CommentsContainer.tsx +++ b/src/components/comments/CommentsContainer.tsx @@ -1,24 +1,23 @@ -import React, {useEffect, useRef, useState} from 'react'; +import moment from 'moment'; +import React, {useContext, useEffect, useRef, useState} from 'react'; import {StyleSheet} from 'react-native'; import {FlatList} from 'react-native-gesture-handler'; import {useDispatch, useSelector} from 'react-redux'; -import CommentTile from './CommentTile'; +import {CommentContext} from '../../screens/profile/MomentCommentsScreen'; import {getComments} from '../../services'; import {updateReplyPosted} from '../../store/actions'; import {RootState} from '../../store/rootReducer'; -import {CommentType, ScreenType, TypeOfComment} from '../../types'; +import {CommentThreadType, CommentType, ScreenType} from '../../types'; import {SCREEN_HEIGHT} from '../../utils'; +import CommentTile from './CommentTile'; + export type CommentsContainerProps = { screenType: ScreenType; - //objectId can be either moment_id or comment_id objectId: string; commentId?: string; - setCommentsLength?: (count: number) => void; - newCommentsAvailable: boolean; - setNewCommentsAvailable: (value: boolean) => void; - typeOfComment: TypeOfComment; - setCommentObjectInFocus?: (comment: CommentType | undefined) => void; - commentObjectInFocus?: CommentType; + shouldUpdate: boolean; + setShouldUpdate: (update: boolean) => void; + isThread: boolean; }; /** @@ -28,14 +27,12 @@ export type CommentsContainerProps = { const CommentsContainer: React.FC = ({ screenType, objectId, - setCommentsLength, - newCommentsAvailable, - setNewCommentsAvailable, - typeOfComment, - setCommentObjectInFocus, - commentObjectInFocus, + isThread, + shouldUpdate, + setShouldUpdate, commentId, }) => { + const {setCommentsLength, commentTapped} = useContext(CommentContext); const {username: loggedInUsername} = useSelector( (state: RootState) => state.user.user, ); @@ -45,57 +42,30 @@ const CommentsContainer: React.FC = ({ useEffect(() => { const loadComments = async () => { - await getComments(objectId, typeOfComment === 'Thread').then( - (comments) => { - if (comments && subscribedToLoadComments) { - setCommentsList(comments); - if (setCommentsLength) { - setCommentsLength(comments.length); - } - setNewCommentsAvailable(false); + await getComments(objectId, isThread).then((comments) => { + if (comments && subscribedToLoadComments) { + setCommentsList(comments); + if (setCommentsLength) { + setCommentsLength(comments.length); } - }, - ); + setShouldUpdate(false); + } + }); }; let subscribedToLoadComments = true; - if (newCommentsAvailable) { + if (shouldUpdate) { loadComments(); } return () => { subscribedToLoadComments = false; }; - }, [ - dispatch, - objectId, - newCommentsAvailable, - setNewCommentsAvailable, - setCommentsLength, - typeOfComment, - ]); - - // eslint-disable-next-line no-shadow - const swapCommentTo = (commentId: string, toIndex: number) => { - const index = commentsList.findIndex( - (item) => item.comment_id === commentId, - ); - if (index > 0) { - let comments = [...commentsList]; - const temp = comments[index]; - comments[index] = comments[toIndex]; - comments[toIndex] = temp; - setCommentsList(comments); - } - }; + }, [shouldUpdate]); useEffect(() => { - //Scroll only if a new comment and not a reply was posted - const shouldScroll = () => - typeOfComment === 'Comment' && !commentObjectInFocus; - const performAction = () => { if (commentId) { swapCommentTo(commentId, 0); - } else if (shouldScroll()) { + } else if (!isThread && !commentTapped) { setTimeout(() => { ref.current?.scrollToEnd({animated: true}); }, 500); @@ -108,41 +78,47 @@ const CommentsContainer: React.FC = ({ //Clean up the reply id present in store return () => { - if (commentId && typeOfComment === 'Thread') { + if (commentId && isThread) { setTimeout(() => { dispatch(updateReplyPosted(undefined)); }, 200); } }; - // eslint-disable-next-line react-hooks/exhaustive-deps }, [commentsList, commentId]); - //WIP : TODO : Bring the comment in focus above the keyboard - // useEffect(() => { - // if (commentObjectInFocus && commentsList.length >= 3) { - // swapCommentTo(commentObjectInFocus.comment_id, 2); - // } - // // eslint-disable-next-line react-hooks/exhaustive-deps - // }, [commentObjectInFocus]); + // eslint-disable-next-line no-shadow + const swapCommentTo = (commentId: string, toIndex: number) => { + const index = commentsList.findIndex( + (item) => item.comment_id === commentId, + ); + if (index > 0) { + let comments = [...commentsList]; + const temp = comments[index]; + comments[index] = comments[toIndex]; + comments[toIndex] = temp; + setCommentsList(comments); + } + }; const ITEM_HEIGHT = SCREEN_HEIGHT / 7.0; - const renderComment = ({item}: {item: CommentType}) => ( + const renderComment = ({item}: {item: CommentType | CommentThreadType}) => ( ); return ( moment(a.date_created).unix() - moment(b.date_created).unix(), + )} ref={ref} keyExtractor={(item, index) => index.toString()} decelerationRate={'fast'} diff --git a/src/screens/profile/MomentCommentsScreen.tsx b/src/screens/profile/MomentCommentsScreen.tsx index 73266a39..1a913e58 100644 --- a/src/screens/profile/MomentCommentsScreen.tsx +++ b/src/screens/profile/MomentCommentsScreen.tsx @@ -7,7 +7,7 @@ import {AddComment} from '../../components/'; import CommentsContainer from '../../components/comments/CommentsContainer'; import {ADD_COMMENT_TEXT} from '../../constants/strings'; import {headerBarOptions, MainStackParams} from '../../routes/main'; -import {CommentType} from '../../types'; +import {CommentThreadType, CommentType} from '../../types'; import {HeaderHeight, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; /** @@ -25,18 +25,35 @@ interface MomentCommentsScreenProps { route: MomentCommentsScreenRouteProps; } +type MomentCommentContextType = { + commentTapped: CommentType | CommentThreadType | undefined; + setCommentTapped: ( + comment: CommentType | CommentThreadType | undefined, + ) => void; + shouldUpdateAllComments: boolean; + setShouldUpdateAllComments: (available: boolean) => void; + commentsLength: number; + setCommentsLength: (length: number) => void; +}; + +export const CommentContext = React.createContext( + {} as MomentCommentContextType, +); + const MomentCommentsScreen: React.FC = ({route}) => { const navigation = useNavigation(); const {moment_id, screenType, comment_id} = route.params; //Receives comment length from child CommentsContainer const [commentsLength, setCommentsLength] = useState(0); - const [newCommentsAvailable, setNewCommentsAvailable] = React.useState(true); + const [shouldUpdateAllComments, setShouldUpdateAllComments] = React.useState( + true, + ); //Keeps track of the current comments object in focus so that the application knows which comment to post a reply to - const [commentObjectInFocus, setCommentObjectInFocus] = useState< - CommentType | undefined - >(undefined); + const [commentTapped, setCommentTapped] = useState< + CommentType | CommentThreadType | undefined + >(); useEffect(() => { navigation.setOptions({ @@ -45,37 +62,39 @@ const MomentCommentsScreen: React.FC = ({route}) => { }, [commentsLength, navigation]); return ( - - - - - - - - - + + + + + + + + + + + ); }; -- cgit v1.2.3-70-g09d2 From c23feea922da063d88d031f25b72b53ba593ec04 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 4 May 2021 20:07:03 -0400 Subject: fixed type warning --- src/components/comments/CommentTile.tsx | 7 +++++-- src/utils/comments.tsx | 10 +++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'src/components/comments/CommentTile.tsx') diff --git a/src/components/comments/CommentTile.tsx b/src/components/comments/CommentTile.tsx index 37a249a8..12f32c95 100644 --- a/src/components/comments/CommentTile.tsx +++ b/src/components/comments/CommentTile.tsx @@ -14,7 +14,7 @@ import {deleteComment, getCommentsCount} from '../../services'; import {RootState} from '../../store/rootReducer'; import {CommentThreadType, CommentType, ScreenType} from '../../types'; import {getTimePosted, normalize, SCREEN_WIDTH} from '../../utils'; -import {mentionPartTypes, renderValue} from '../../utils/comments'; +import {mentionPartTypes, renderTextWithMentions} from '../../utils/comments'; import {ProfilePreview} from '../profile'; import CommentsContainer from './CommentsContainer'; @@ -136,7 +136,10 @@ const CommentTile: React.FC = ({ /> - {renderValue(commentObject.comment, mentionPartTypes)} + {renderTextWithMentions({ + value: commentObject.comment, + partTypes: mentionPartTypes, + })} diff --git a/src/utils/comments.tsx b/src/utils/comments.tsx index a1f353d6..c0b522f2 100644 --- a/src/utils/comments.tsx +++ b/src/utils/comments.tsx @@ -43,6 +43,11 @@ const renderPart = (part: Part, index: number) => { ); }; +interface RenderProps { + value: string; + partTypes: PartType[]; +} + /** * Value renderer. Parsing value to parts array and then mapping the array using 'renderPart' * @@ -51,7 +56,10 @@ const renderPart = (part: Part, index: number) => { * @param value - value from MentionInput * @param partTypes - the part types array that you providing to MentionInput */ -export const renderValue: React.FC = (value: string, partTypes: PartType[]) => { +export const renderTextWithMentions: React.FC = ({ + value, + partTypes, +}) => { const {parts} = parseValue(value, partTypes); return {parts.map(renderPart)}; }; -- cgit v1.2.3-70-g09d2 From 7f9799c6693254f8eb6729c0977826694c28c437 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Tue, 4 May 2021 20:46:27 -0400 Subject: added navigation to profile --- src/components/comments/CommentTile.tsx | 34 ++++++++++++++------ src/components/moments/MomentPostContent.tsx | 23 ++++++++++++-- src/screens/profile/CaptionScreen.tsx | 17 +++++----- src/utils/comments.tsx | 46 ++++++++++++++++++---------- src/utils/users.ts | 24 +++++++++++++++ 5 files changed, 105 insertions(+), 39 deletions(-) (limited to 'src/components/comments/CommentTile.tsx') diff --git a/src/components/comments/CommentTile.tsx b/src/components/comments/CommentTile.tsx index 12f32c95..ce346af5 100644 --- a/src/components/comments/CommentTile.tsx +++ b/src/components/comments/CommentTile.tsx @@ -1,9 +1,10 @@ +import {useNavigation} from '@react-navigation/native'; import React, {Fragment, useContext, useEffect, useRef, useState} from 'react'; import {Alert, Animated, StyleSheet} from 'react-native'; import {Text, View} from 'react-native-animatable'; import {RectButton, TouchableOpacity} from 'react-native-gesture-handler'; import Swipeable from 'react-native-gesture-handler/Swipeable'; -import {useSelector} from 'react-redux'; +import {useDispatch, useSelector, useStore} from 'react-redux'; import Arrow from '../../assets/icons/back-arrow-colored.svg'; import ClockIcon from '../../assets/icons/clock-icon-01.svg'; import Trash from '../../assets/ionicons/trash-outline.svg'; @@ -12,9 +13,19 @@ import {ERROR_FAILED_TO_DELETE_COMMENT} from '../../constants/strings'; import {CommentContext} from '../../screens/profile/MomentCommentsScreen'; import {deleteComment, getCommentsCount} from '../../services'; import {RootState} from '../../store/rootReducer'; -import {CommentThreadType, CommentType, ScreenType} from '../../types'; -import {getTimePosted, normalize, SCREEN_WIDTH} from '../../utils'; -import {mentionPartTypes, renderTextWithMentions} from '../../utils/comments'; +import { + CommentThreadType, + CommentType, + ScreenType, + UserType, +} from '../../types'; +import { + getTimePosted, + navigateToProfile, + normalize, + SCREEN_WIDTH, +} from '../../utils'; +import {renderTextWithMentions} from '../../utils/comments'; import {ProfilePreview} from '../profile'; import CommentsContainer from './CommentsContainer'; @@ -46,6 +57,9 @@ const CommentTile: React.FC = ({ const [shouldUpdateChild, setShouldUpdateChild] = useState(true); const swipeRef = useRef(null); const {replyPosted} = useSelector((state: RootState) => state.user); + const state: RootState = useStore().getState(); + const navigation = useNavigation(); + const dispatch = useDispatch(); useEffect(() => { if (shouldUpdateParent) { @@ -135,12 +149,12 @@ const CommentTile: React.FC = ({ screenType={screenType} /> - - {renderTextWithMentions({ - value: commentObject.comment, - partTypes: mentionPartTypes, - })} - + {renderTextWithMentions({ + value: commentObject.comment, + styles: styles.comment, + onPress: (user: UserType) => + navigateToProfile(state, dispatch, navigation, screenType, user), + })} {' ' + timePosted} diff --git a/src/components/moments/MomentPostContent.tsx b/src/components/moments/MomentPostContent.tsx index d68ceaa3..03034925 100644 --- a/src/components/moments/MomentPostContent.tsx +++ b/src/components/moments/MomentPostContent.tsx @@ -1,8 +1,17 @@ +import {useNavigation} from '@react-navigation/native'; import React, {useEffect} from 'react'; import {Image, StyleSheet, Text, View, ViewProps} from 'react-native'; +import {useDispatch, useStore} from 'react-redux'; import {getCommentsCount} from '../../services'; -import {ScreenType} from '../../types'; -import {getTimePosted, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; +import {RootState} from '../../store/rootReducer'; +import {ScreenType, UserType} from '../../types'; +import { + getTimePosted, + navigateToProfile, + SCREEN_HEIGHT, + SCREEN_WIDTH, +} from '../../utils'; +import {renderTextWithMentions} from '../../utils/comments'; import {CommentsCount} from '../comments'; interface MomentPostContentProps extends ViewProps { @@ -22,6 +31,9 @@ const MomentPostContent: React.FC = ({ }) => { const [elapsedTime, setElapsedTime] = React.useState(); const [comments_count, setCommentsCount] = React.useState(''); + const state: RootState = useStore().getState(); + const navigation = useNavigation(); + const dispatch = useDispatch(); useEffect(() => { const fetchCommentsCount = async () => { @@ -47,7 +59,12 @@ const MomentPostContent: React.FC = ({ /> {elapsedTime} - {caption} + {renderTextWithMentions({ + value: caption, + styles: styles.captionText, + onPress: (user: UserType) => + navigateToProfile(state, dispatch, navigation, screenType, user), + })} ); }; diff --git a/src/screens/profile/CaptionScreen.tsx b/src/screens/profile/CaptionScreen.tsx index 156ee41c..041f0da2 100644 --- a/src/screens/profile/CaptionScreen.tsx +++ b/src/screens/profile/CaptionScreen.tsx @@ -11,9 +11,10 @@ import { TouchableWithoutFeedback, View, } from 'react-native'; +import {MentionInput} from 'react-native-controlled-mentions'; import {Button} from 'react-native-elements'; import {useDispatch, useSelector} from 'react-redux'; -import {SearchBackground, TaggBigInput} from '../../components'; +import {SearchBackground} from '../../components'; import {CaptionScreenHeader} from '../../components/'; import TaggLoadingIndicator from '../../components/common/TaggLoadingIndicator'; import {TAGG_LIGHT_BLUE_2} from '../../constants'; @@ -26,6 +27,7 @@ import { } from '../../store/actions'; import {RootState} from '../../store/rootReducer'; import {SCREEN_WIDTH, StatusBarHeight} from '../../utils'; +import {mentionPartTypes} from '../../utils/comments'; /** * Upload Screen to allow users to upload posts to Tagg @@ -49,10 +51,6 @@ const CaptionScreen: React.FC = ({route, navigation}) => { const [caption, setCaption] = useState(''); const [loading, setLoading] = useState(false); - const handleCaptionUpdate = (newCaption: string) => { - setCaption(newCaption); - }; - const navigateToProfile = () => { //Since the logged In User is navigating to own profile, useXId is not required navigation.navigate('Profile', { @@ -112,12 +110,13 @@ const CaptionScreen: React.FC = ({route, navigation}) => { source={{uri: image.path}} resizeMode={'cover'} /> - diff --git a/src/utils/comments.tsx b/src/utils/comments.tsx index c0b522f2..47d26bb5 100644 --- a/src/utils/comments.tsx +++ b/src/utils/comments.tsx @@ -1,23 +1,26 @@ import React from 'react'; -import {Text} from 'react-native'; +import {StyleProp, Text, TextStyle} from 'react-native'; import { isMentionPartType, parseValue, Part, PartType, } from 'react-native-controlled-mentions'; +import {TouchableOpacity} from 'react-native-gesture-handler'; import TaggTypeahead from '../components/common/TaggTypeahead'; import {TAGG_LIGHT_BLUE} from '../constants'; +import {UserType} from '../types'; /** * Part renderer * * https://github.com/dabakovich/react-native-controlled-mentions#rendering-mentioninputs-value - * - * @param part - * @param index */ -const renderPart = (part: Part, index: number) => { +const renderPart = ( + part: Part, + index: number, + handlePress: (user: UserType) => void, +) => { // Just plain text if (!part.partType) { return {part.text}; @@ -26,12 +29,18 @@ const renderPart = (part: Part, index: number) => { // Mention type part if (isMentionPartType(part.partType)) { return ( - console.log('Pressed', part.data)}> - {part.text} - + onPress={() => { + if (part.data) { + handlePress({ + userId: part.data.id, + username: part.data.name, + }); + } + }}> + {part.text} + ); } @@ -45,23 +54,26 @@ const renderPart = (part: Part, index: number) => { interface RenderProps { value: string; - partTypes: PartType[]; + styles: StyleProp; + onPress: (user: UserType) => void; } /** * Value renderer. Parsing value to parts array and then mapping the array using 'renderPart' * * https://github.com/dabakovich/react-native-controlled-mentions#rendering-mentioninputs-value - * - * @param value - value from MentionInput - * @param partTypes - the part types array that you providing to MentionInput */ export const renderTextWithMentions: React.FC = ({ value, - partTypes, + styles, + onPress, }) => { - const {parts} = parseValue(value, partTypes); - return {parts.map(renderPart)}; + const {parts} = parseValue(value, mentionPartTypes); + return ( + + {parts.map((part, index) => renderPart(part, index, onPress))} + + ); }; export const mentionPartTypes: PartType[] = [ diff --git a/src/utils/users.ts b/src/utils/users.ts index abadaf6e..c167ac2f 100644 --- a/src/utils/users.ts +++ b/src/utils/users.ts @@ -1,4 +1,6 @@ import AsyncStorage from '@react-native-community/async-storage'; +import {NavigationProp} from '@react-navigation/core'; +import {Dispatch} from 'react'; import {INTEGRATED_SOCIAL_LIST} from '../constants'; import {isUserBlocked, loadSocialPosts} from '../services'; import { @@ -199,3 +201,25 @@ export const canViewProfile = ( } return false; }; + +export const navigateToProfile = async ( + state: RootState, + dispatch: any, + navigation: any, + screenType: ScreenType, + user: UserType, +) => { + const loggedInUserId = state.user.user.userId; + const {userId, username} = user; + if (!userXInStore(state, screenType, userId)) { + await fetchUserX( + dispatch, + {userId: userId, username: username}, + screenType, + ); + } + navigation.push('Profile', { + userXId: userId === loggedInUserId ? undefined : userId, + screenType, + }); +}; -- cgit v1.2.3-70-g09d2