From 9322aede82815ba5a6bddf5b289692955d6e1d96 Mon Sep 17 00:00:00 2001
From: Ivan Chen <ivan@tagg.id>
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<CommentTileProps> = ({
           screenType={screenType}
         />
         <TouchableOpacity style={styles.body} onPress={toggleAddComment}>
-          <Text style={styles.comment}>{comment_object.comment}</Text>
+          <View style={styles.comment}>
+            {renderValue(comment_object.comment, mentionPartTypes)}
+          </View>
           <View style={styles.clockIconAndTime}>
             <ClockIcon style={styles.clockIcon} />
             <Text style={styles.date_time}>{' ' + timePosted}</Text>
-- 
cgit v1.2.3-70-g09d2


From 2832decea88ed14325f92759617ae8ee8a588d22 Mon Sep 17 00:00:00 2001
From: Ivan Chen <ivan@tagg.id>
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<AddCommentProps> = ({
-  setNewCommentsAvailable,
-  commentInReplyTo,
-  momentId,
-  placeholderText,
-  isReplying,
-}) => {
+const AddComment: React.FC<AddCommentProps> = ({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<TextInput>(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<AddCommentProps> = ({
           }),
         );
       }
-      setNewCommentsAvailable(true);
+      setShouldUpdateAllComments(true);
     }
   };
 
@@ -91,15 +89,17 @@ const AddComment: React.FC<AddCommentProps> = ({
   }, []);
 
   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 (
     <KeyboardAvoidingView
diff --git a/src/components/comments/CommentTile.tsx b/src/components/comments/CommentTile.tsx
index 334934f1..37a249a8 100644
--- a/src/components/comments/CommentTile.tsx
+++ b/src/components/comments/CommentTile.tsx
@@ -1,5 +1,4 @@
-/* eslint-disable radix */
-import React, {Fragment, useEffect, useRef, useState} from 'react';
+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';
@@ -10,9 +9,10 @@ import ClockIcon from '../../assets/icons/clock-icon-01.svg';
 import Trash from '../../assets/ionicons/trash-outline.svg';
 import {TAGG_LIGHT_BLUE} from '../../constants';
 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 {CommentType, ScreenType, TypeOfComment} from '../../types';
+import {CommentThreadType, CommentType, ScreenType} from '../../types';
 import {getTimePosted, normalize, SCREEN_WIDTH} from '../../utils';
 import {mentionPartTypes, renderValue} from '../../utils/comments';
 import {ProfilePreview} from '../profile';
@@ -23,98 +23,78 @@ import CommentsContainer from './CommentsContainer';
  */
 
 interface CommentTileProps {
-  comment_object: CommentType;
+  commentObject: CommentType | CommentThreadType;
   screenType: ScreenType;
-  typeOfComment: TypeOfComment;
-  setCommentObjectInFocus?: (comment: CommentType | undefined) => void;
-  newCommentsAvailable: boolean;
-  setNewCommentsAvailable: (available: boolean) => void;
+  isThread: boolean;
+  shouldUpdateParent: boolean;
+  setShouldUpdateParent: (update: boolean) => void;
   canDelete: boolean;
 }
 
 const CommentTile: React.FC<CommentTileProps> = ({
-  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<boolean>(false);
   const [showKeyboard, setShowKeyboard] = useState<boolean>(false);
-  const [newThreadAvailable, setNewThreadAvailable] = useState(true);
+  const [shouldUpdateChild, setShouldUpdateChild] = useState(true);
   const swipeRef = useRef<Swipeable>(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<CommentTileProps> = ({
       <View
         style={[styles.container, isThread ? styles.moreMarginWithThread : {}]}>
         <ProfilePreview
-          profilePreview={comment_object.commenter}
+          profilePreview={commentObject.commenter}
           previewType={'Comment'}
           screenType={screenType}
         />
         <TouchableOpacity style={styles.body} onPress={toggleAddComment}>
           <View style={styles.comment}>
-            {renderValue(comment_object.comment, mentionPartTypes)}
+            {renderValue(commentObject.comment, mentionPartTypes)}
           </View>
           <View style={styles.clockIconAndTime}>
             <ClockIcon style={styles.clockIcon} />
@@ -165,11 +145,13 @@ const CommentTile: React.FC<CommentTileProps> = ({
           </View>
         </TouchableOpacity>
         {/*** Show replies text only if there are some replies present */}
-        {typeOfComment === 'Comment' && comment_object.replies_count > 0 && (
+        {!isThread && (commentObject as CommentType).replies_count > 0 && (
           <TouchableOpacity
             style={styles.repliesTextAndIconContainer}
             onPress={toggleReplies}>
-            <Text style={styles.repliesText}>{getRepliesText()}</Text>
+            <Text style={styles.repliesText}>
+              {getRepliesText(commentObject as CommentType)}
+            </Text>
             <Arrow
               width={12}
               height={11}
@@ -186,12 +168,11 @@ const CommentTile: React.FC<CommentTileProps> = ({
       {showReplies && (
         <View>
           <CommentsContainer
-            objectId={comment_object.comment_id}
+            objectId={commentObject.comment_id}
             screenType={screenType}
-            setNewCommentsAvailable={setNewThreadAvailable}
-            newCommentsAvailable={newThreadAvailable}
-            typeOfComment={'Thread'}
-            commentId={replyPosted?.comment_id}
+            shouldUpdate={shouldUpdateChild}
+            setShouldUpdate={setShouldUpdateChild}
+            isThread={true}
           />
         </View>
       )}
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<CommentsContainerProps> = ({
   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<CommentsContainerProps> = ({
 
   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<CommentsContainerProps> = ({
 
     //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}) => (
     <CommentTile
       key={item.comment_id}
-      comment_object={item}
+      commentObject={item}
       screenType={screenType}
-      typeOfComment={typeOfComment}
-      setCommentObjectInFocus={setCommentObjectInFocus}
-      newCommentsAvailable={newCommentsAvailable}
-      setNewCommentsAvailable={setNewCommentsAvailable}
+      isThread={isThread}
+      shouldUpdateParent={shouldUpdate}
+      setShouldUpdateParent={setShouldUpdate}
       canDelete={item.commenter.username === loggedInUsername}
     />
   );
 
   return (
     <FlatList
-      data={commentsList}
+      data={commentsList.sort(
+        (a, b) => 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<MomentCommentsScreenProps> = ({route}) => {
   const navigation = useNavigation();
   const {moment_id, screenType, comment_id} = route.params;
 
   //Receives comment length from child CommentsContainer
   const [commentsLength, setCommentsLength] = useState<number>(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<MomentCommentsScreenProps> = ({route}) => {
   }, [commentsLength, navigation]);
 
   return (
-    <View style={styles.background}>
-      <SafeAreaView>
-        <View style={styles.body}>
-          <CommentsContainer
-            objectId={moment_id}
-            commentId={comment_id}
-            screenType={screenType}
-            setCommentsLength={setCommentsLength}
-            newCommentsAvailable={newCommentsAvailable}
-            setNewCommentsAvailable={setNewCommentsAvailable}
-            setCommentObjectInFocus={setCommentObjectInFocus}
-            commentObjectInFocus={commentObjectInFocus}
-            typeOfComment={'Comment'}
-          />
-          <AddComment
-            placeholderText={
-              commentObjectInFocus
-                ? ADD_COMMENT_TEXT(commentObjectInFocus.commenter.username)
-                : ADD_COMMENT_TEXT()
-            }
-            setNewCommentsAvailable={setNewCommentsAvailable}
-            commentInReplyTo={
-              commentObjectInFocus ? commentObjectInFocus : undefined
-            }
-            momentId={commentObjectInFocus ? undefined : moment_id}
-            isReplying={commentObjectInFocus ? true : false}
-          />
-        </View>
-      </SafeAreaView>
-      <TabsGradient />
-    </View>
+    <CommentContext.Provider
+      value={{
+        commentTapped,
+        setCommentTapped,
+        shouldUpdateAllComments,
+        setShouldUpdateAllComments,
+        commentsLength,
+        setCommentsLength,
+      }}>
+      <View style={styles.background}>
+        <SafeAreaView>
+          <View style={styles.body}>
+            <CommentsContainer
+              objectId={moment_id}
+              commentId={comment_id}
+              screenType={screenType}
+              shouldUpdate={shouldUpdateAllComments}
+              setShouldUpdate={setShouldUpdateAllComments}
+              isThread={false}
+            />
+            <AddComment
+              placeholderText={
+                !commentTapped
+                  ? ADD_COMMENT_TEXT()
+                  : ADD_COMMENT_TEXT(commentTapped.commenter.username)
+              }
+              momentId={moment_id}
+            />
+          </View>
+        </SafeAreaView>
+        <TabsGradient />
+      </View>
+    </CommentContext.Provider>
   );
 };
 
-- 
cgit v1.2.3-70-g09d2


From c23feea922da063d88d031f25b72b53ba593ec04 Mon Sep 17 00:00:00 2001
From: Ivan Chen <ivan@tagg.id>
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<CommentTileProps> = ({
         />
         <TouchableOpacity style={styles.body} onPress={toggleAddComment}>
           <View style={styles.comment}>
-            {renderValue(commentObject.comment, mentionPartTypes)}
+            {renderTextWithMentions({
+              value: commentObject.comment,
+              partTypes: mentionPartTypes,
+            })}
           </View>
           <View style={styles.clockIconAndTime}>
             <ClockIcon style={styles.clockIcon} />
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<RenderProps> = ({
+  value,
+  partTypes,
+}) => {
   const {parts} = parseValue(value, partTypes);
   return <Text>{parts.map(renderPart)}</Text>;
 };
-- 
cgit v1.2.3-70-g09d2


From 7f9799c6693254f8eb6729c0977826694c28c437 Mon Sep 17 00:00:00 2001
From: Ivan Chen <ivan@tagg.id>
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<CommentTileProps> = ({
   const [shouldUpdateChild, setShouldUpdateChild] = useState(true);
   const swipeRef = useRef<Swipeable>(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<CommentTileProps> = ({
           screenType={screenType}
         />
         <TouchableOpacity style={styles.body} onPress={toggleAddComment}>
-          <View style={styles.comment}>
-            {renderTextWithMentions({
-              value: commentObject.comment,
-              partTypes: mentionPartTypes,
-            })}
-          </View>
+          {renderTextWithMentions({
+            value: commentObject.comment,
+            styles: styles.comment,
+            onPress: (user: UserType) =>
+              navigateToProfile(state, dispatch, navigation, screenType, user),
+          })}
           <View style={styles.clockIconAndTime}>
             <ClockIcon style={styles.clockIcon} />
             <Text style={styles.date_time}>{' ' + timePosted}</Text>
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<MomentPostContentProps> = ({
 }) => {
   const [elapsedTime, setElapsedTime] = React.useState<string>();
   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<MomentPostContentProps> = ({
         />
         <Text style={styles.text}>{elapsedTime}</Text>
       </View>
-      <Text style={styles.captionText}>{caption}</Text>
+      {renderTextWithMentions({
+        value: caption,
+        styles: styles.captionText,
+        onPress: (user: UserType) =>
+          navigateToProfile(state, dispatch, navigation, screenType, user),
+      })}
     </View>
   );
 };
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<CaptionScreenProps> = ({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<CaptionScreenProps> = ({route, navigation}) => {
               source={{uri: image.path}}
               resizeMode={'cover'}
             />
-            <TaggBigInput
-              style={styles.text}
-              multiline
+            <MentionInput
+              containerStyle={styles.text}
               placeholder="Write something....."
               placeholderTextColor="gray"
-              onChangeText={handleCaptionUpdate}
+              value={caption}
+              onChange={setCaption}
+              partTypes={mentionPartTypes}
             />
           </View>
         </KeyboardAvoidingView>
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 <Text key={index}>{part.text}</Text>;
@@ -26,12 +29,18 @@ const renderPart = (part: Part, index: number) => {
   // Mention type part
   if (isMentionPartType(part.partType)) {
     return (
-      <Text
+      <TouchableOpacity
         key={`${index}-${part.data?.trigger}`}
-        style={part.partType.textStyle}
-        onPress={() => console.log('Pressed', part.data)}>
-        {part.text}
-      </Text>
+        onPress={() => {
+          if (part.data) {
+            handlePress({
+              userId: part.data.id,
+              username: part.data.name,
+            });
+          }
+        }}>
+        <Text style={part.partType.textStyle}>{part.text}</Text>
+      </TouchableOpacity>
     );
   }
 
@@ -45,23 +54,26 @@ const renderPart = (part: Part, index: number) => {
 
 interface RenderProps {
   value: string;
-  partTypes: PartType[];
+  styles: StyleProp<TextStyle>;
+  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<RenderProps> = ({
   value,
-  partTypes,
+  styles,
+  onPress,
 }) => {
-  const {parts} = parseValue(value, partTypes);
-  return <Text>{parts.map(renderPart)}</Text>;
+  const {parts} = parseValue(value, mentionPartTypes);
+  return (
+    <Text style={styles}>
+      {parts.map((part, index) => renderPart(part, index, onPress))}
+    </Text>
+  );
 };
 
 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

