From f0c5a226d4ae02cd92d92372708056f63d3cd976 Mon Sep 17 00:00:00 2001
From: Ivan Chen <ivan@tagg.id>
Date: Thu, 29 Apr 2021 16:32:30 -0400
Subject: added ParsedText, initial work for typeahead

---
 src/components/comments/AddComment.tsx | 48 ++++++++++++++++++++++++++++------
 1 file changed, 40 insertions(+), 8 deletions(-)

(limited to 'src/components/comments/AddComment.tsx')

diff --git a/src/components/comments/AddComment.tsx b/src/components/comments/AddComment.tsx
index 2a8c773e..79d4d970 100644
--- a/src/components/comments/AddComment.tsx
+++ b/src/components/comments/AddComment.tsx
@@ -1,12 +1,14 @@
-import React, {useEffect, useRef} from 'react';
+import React, {useEffect, useRef, useState} from 'react';
 import {
   Keyboard,
   KeyboardAvoidingView,
   Platform,
   StyleSheet,
+  TextInput,
   View,
 } from 'react-native';
-import {TextInput, TouchableOpacity} from 'react-native-gesture-handler';
+import {TouchableOpacity} from 'react-native-gesture-handler';
+import ParsedText from 'react-native-parsed-text';
 import {useDispatch, useSelector} from 'react-redux';
 import UpArrowIcon from '../../assets/icons/up_arrow.svg';
 import {TAGG_LIGHT_BLUE} from '../../constants';
@@ -14,7 +16,7 @@ import {postComment} from '../../services';
 import {updateReplyPosted} from '../../store/actions';
 import {RootState} from '../../store/rootreducer';
 import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
-import {Avatar} from '../common';
+import {Avatar, TaggTypeahead} from '../common';
 
 /**
  * This file provides the add comment view for a user.
@@ -36,7 +38,9 @@ const AddComment: React.FC<AddCommentProps> = ({
   isCommentInFocus,
 }) => {
   const [comment, setComment] = React.useState('');
-  const [keyboardVisible, setKeyboardVisible] = React.useState(false);
+  const [keyboardVisible, setKeyboardVisible] = useState(false);
+  const [isMentioning, setIsMentioning] = useState(false);
+  const [mentionSearch, setMentionSearch] = useState('');
 
   const {avatar} = useSelector((state: RootState) => state.user);
   const dispatch = useDispatch();
@@ -95,6 +99,7 @@ const AddComment: React.FC<AddCommentProps> = ({
     <KeyboardAvoidingView
       behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
       keyboardVerticalOffset={SCREEN_HEIGHT * 0.1}>
+      {isMentioning && <TaggTypeahead search={mentionSearch} />}
       <View
         style={[
           styles.container,
@@ -106,11 +111,37 @@ const AddComment: React.FC<AddCommentProps> = ({
             style={styles.text}
             placeholder={placeholderText}
             placeholderTextColor="grey"
-            onChangeText={setComment}
-            value={comment}
+            onChangeText={(newText: string) => {
+              const newestChar = newText[newText.length - 1];
+              if (newestChar === ' ') {
+                setIsMentioning(false);
+                setMentionSearch('');
+              }
+              if (newestChar === '@') {
+                setIsMentioning(true);
+              }
+              if (isMentioning) {
+                const match = newText.match(/@(.*)$/);
+                if (match) {
+                  setMentionSearch(match[1]);
+                }
+              }
+              setComment(newText);
+            }}
             multiline={true}
-            ref={ref}
-          />
+            ref={ref}>
+            <ParsedText
+              style={styles.text}
+              parse={[
+                {
+                  pattern: /@\{.*\}/,
+                  style: {color: 'red'},
+                  renderText: (text: string) => '@fooo',
+                },
+              ]}>
+              {comment}
+            </ParsedText>
+          </TextInput>
           <View style={styles.submitButton}>
             <TouchableOpacity style={styles.submitButton} onPress={addComment}>
               <UpArrowIcon width={35} height={35} color={'white'} />
@@ -141,6 +172,7 @@ const styles = StyleSheet.create({
     flex: 1,
     padding: '1%',
     marginHorizontal: '1%',
+    maxHeight: 100,
   },
   avatar: {
     height: 35,
-- 
cgit v1.2.3-70-g09d2


From b7482f61fc5c9d62651842e7647d2ff95d47c785 Mon Sep 17 00:00:00 2001
From: Ivan Chen <ivan@tagg.id>
Date: Thu, 29 Apr 2021 19:35:42 -0400
Subject: finished logic with bugs

---
 src/components/comments/AddComment.tsx | 67 ++++++++++++++++++++++++----------
 1 file changed, 47 insertions(+), 20 deletions(-)

(limited to 'src/components/comments/AddComment.tsx')

diff --git a/src/components/comments/AddComment.tsx b/src/components/comments/AddComment.tsx
index 79d4d970..97c87299 100644
--- a/src/components/comments/AddComment.tsx
+++ b/src/components/comments/AddComment.tsx
@@ -8,13 +8,14 @@ import {
   View,
 } from 'react-native';
 import {TouchableOpacity} from 'react-native-gesture-handler';
-import ParsedText from 'react-native-parsed-text';
+import ParsedText, {ParseShape} from 'react-native-parsed-text';
 import {useDispatch, useSelector} from 'react-redux';
 import UpArrowIcon from '../../assets/icons/up_arrow.svg';
 import {TAGG_LIGHT_BLUE} from '../../constants';
 import {postComment} from '../../services';
 import {updateReplyPosted} from '../../store/actions';
 import {RootState} from '../../store/rootreducer';
+import {ProfilePreviewType} from '../../types';
 import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
 import {Avatar, TaggTypeahead} from '../common';
 
@@ -37,13 +38,16 @@ const AddComment: React.FC<AddCommentProps> = ({
   placeholderText,
   isCommentInFocus,
 }) => {
-  const [comment, setComment] = React.useState('');
+  const [comment, setComment] = useState('');
   const [keyboardVisible, setKeyboardVisible] = useState(false);
   const [isMentioning, setIsMentioning] = useState(false);
-  const [mentionSearch, setMentionSearch] = useState('');
-
+  const [mentionQuery, setMentionQuery] = useState('');
+  const [selectedMention, setSelectedMention] = useState<ProfilePreviewType>();
+  const [mentions, setMentions] = useState<ProfilePreviewType[]>([]);
   const {avatar} = useSelector((state: RootState) => state.user);
   const dispatch = useDispatch();
+  const ref = useRef<TextInput>(null);
+  const [parsePatterns, setParsePatterns] = useState<ParseShape[]>([]);
 
   const addComment = async () => {
     const trimmed = comment.trim();
@@ -74,6 +78,30 @@ const AddComment: React.FC<AddCommentProps> = ({
     }
   };
 
+  useEffect(() => {
+    setParsePatterns(
+      mentions.map((m) => ({
+        pattern: new RegExp(`@${m.username}`),
+        style: {color: TAGG_LIGHT_BLUE},
+      })),
+    );
+  }, [mentions]);
+
+  useEffect(() => {
+    if (selectedMention) {
+      setComment(
+        comment.replace(
+          new RegExp(`@${mentionQuery}`),
+          `@${selectedMention.username} `,
+        ),
+      );
+      setMentions([...mentions, selectedMention]);
+      setSelectedMention(undefined);
+      setMentionQuery('');
+      setIsMentioning(false);
+    }
+  }, [selectedMention]);
+
   useEffect(() => {
     const showKeyboard = () => setKeyboardVisible(true);
     Keyboard.addListener('keyboardWillShow', showKeyboard);
@@ -86,8 +114,6 @@ const AddComment: React.FC<AddCommentProps> = ({
     return () => Keyboard.removeListener('keyboardWillHide', hideKeyboard);
   }, []);
 
-  const ref = useRef<TextInput>(null);
-
   //If a comment is in Focus, bring the keyboard up so user is able to type in a reply
   useEffect(() => {
     if (isCommentInFocus) {
@@ -99,7 +125,12 @@ const AddComment: React.FC<AddCommentProps> = ({
     <KeyboardAvoidingView
       behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
       keyboardVerticalOffset={SCREEN_HEIGHT * 0.1}>
-      {isMentioning && <TaggTypeahead search={mentionSearch} />}
+      {isMentioning && (
+        <TaggTypeahead
+          query={mentionQuery}
+          setSelectedMention={setSelectedMention}
+        />
+      )}
       <View
         style={[
           styles.container,
@@ -113,32 +144,28 @@ const AddComment: React.FC<AddCommentProps> = ({
             placeholderTextColor="grey"
             onChangeText={(newText: string) => {
               const newestChar = newText[newText.length - 1];
-              if (newestChar === ' ') {
+              const deletedChar =
+                newText.length === comment.length - 1
+                  ? comment[comment.length - 1]
+                  : undefined;
+              if (newestChar === ' ' || deletedChar === '@') {
                 setIsMentioning(false);
-                setMentionSearch('');
+                setMentionQuery('');
               }
               if (newestChar === '@') {
                 setIsMentioning(true);
               }
               if (isMentioning) {
-                const match = newText.match(/@(.*)$/);
+                const match = newText.match(/.*@(.*)$/);
                 if (match) {
-                  setMentionSearch(match[1]);
+                  setMentionQuery(match[1]);
                 }
               }
               setComment(newText);
             }}
             multiline={true}
             ref={ref}>
-            <ParsedText
-              style={styles.text}
-              parse={[
-                {
-                  pattern: /@\{.*\}/,
-                  style: {color: 'red'},
-                  renderText: (text: string) => '@fooo',
-                },
-              ]}>
+            <ParsedText style={styles.text} parse={parsePatterns}>
               {comment}
             </ParsedText>
           </TextInput>
-- 
cgit v1.2.3-70-g09d2


From a665c527cf0ab24b1a19b2ed35f38b9acb79cdeb Mon Sep 17 00:00:00 2001
From: Ivan Chen <ivan@tagg.id>
Date: Fri, 30 Apr 2021 15:16:32 -0400
Subject: removed old code, added controlled-mention

---
 package.json                            |  3 +-
 src/components/comments/AddComment.tsx  | 83 +++++++--------------------------
 src/components/common/TaggTypeahead.tsx | 37 +++++++++------
 3 files changed, 41 insertions(+), 82 deletions(-)

(limited to 'src/components/comments/AddComment.tsx')

diff --git a/package.json b/package.json
index b3d69c76..d3c02059 100644
--- a/package.json
+++ b/package.json
@@ -32,6 +32,7 @@
     "react-native-animatable": "^1.3.3",
     "react-native-confirmation-code-field": "^6.5.0",
     "react-native-contacts": "^6.0.4",
+    "react-native-controlled-mentions": "^2.2.5",
     "react-native-date-picker": "^3.2.5",
     "react-native-device-info": "^7.3.1",
     "react-native-document-picker": "^5.0.3",
@@ -102,4 +103,4 @@
       "./node_modules/react-native-gesture-handler/jestSetup.js"
     ]
   }
-}
+}
\ No newline at end of file
diff --git a/src/components/comments/AddComment.tsx b/src/components/comments/AddComment.tsx
index 97c87299..7576675e 100644
--- a/src/components/comments/AddComment.tsx
+++ b/src/components/comments/AddComment.tsx
@@ -7,15 +7,14 @@ import {
   TextInput,
   View,
 } from 'react-native';
+import {MentionInput} from 'react-native-controlled-mentions';
 import {TouchableOpacity} from 'react-native-gesture-handler';
-import ParsedText, {ParseShape} from 'react-native-parsed-text';
 import {useDispatch, useSelector} from 'react-redux';
 import UpArrowIcon from '../../assets/icons/up_arrow.svg';
 import {TAGG_LIGHT_BLUE} from '../../constants';
 import {postComment} from '../../services';
 import {updateReplyPosted} from '../../store/actions';
 import {RootState} from '../../store/rootreducer';
-import {ProfilePreviewType} from '../../types';
 import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
 import {Avatar, TaggTypeahead} from '../common';
 
@@ -40,14 +39,9 @@ const AddComment: React.FC<AddCommentProps> = ({
 }) => {
   const [comment, setComment] = useState('');
   const [keyboardVisible, setKeyboardVisible] = useState(false);
-  const [isMentioning, setIsMentioning] = useState(false);
-  const [mentionQuery, setMentionQuery] = useState('');
-  const [selectedMention, setSelectedMention] = useState<ProfilePreviewType>();
-  const [mentions, setMentions] = useState<ProfilePreviewType[]>([]);
   const {avatar} = useSelector((state: RootState) => state.user);
   const dispatch = useDispatch();
   const ref = useRef<TextInput>(null);
-  const [parsePatterns, setParsePatterns] = useState<ParseShape[]>([]);
 
   const addComment = async () => {
     const trimmed = comment.trim();
@@ -78,30 +72,6 @@ const AddComment: React.FC<AddCommentProps> = ({
     }
   };
 
-  useEffect(() => {
-    setParsePatterns(
-      mentions.map((m) => ({
-        pattern: new RegExp(`@${m.username}`),
-        style: {color: TAGG_LIGHT_BLUE},
-      })),
-    );
-  }, [mentions]);
-
-  useEffect(() => {
-    if (selectedMention) {
-      setComment(
-        comment.replace(
-          new RegExp(`@${mentionQuery}`),
-          `@${selectedMention.username} `,
-        ),
-      );
-      setMentions([...mentions, selectedMention]);
-      setSelectedMention(undefined);
-      setMentionQuery('');
-      setIsMentioning(false);
-    }
-  }, [selectedMention]);
-
   useEffect(() => {
     const showKeyboard = () => setKeyboardVisible(true);
     Keyboard.addListener('keyboardWillShow', showKeyboard);
@@ -125,12 +95,6 @@ const AddComment: React.FC<AddCommentProps> = ({
     <KeyboardAvoidingView
       behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
       keyboardVerticalOffset={SCREEN_HEIGHT * 0.1}>
-      {isMentioning && (
-        <TaggTypeahead
-          query={mentionQuery}
-          setSelectedMention={setSelectedMention}
-        />
-      )}
       <View
         style={[
           styles.container,
@@ -138,37 +102,22 @@ const AddComment: React.FC<AddCommentProps> = ({
         ]}>
         <View style={styles.textContainer}>
           <Avatar style={styles.avatar} uri={avatar} />
-          <TextInput
-            style={styles.text}
+          <MentionInput
+            containerStyle={styles.text}
             placeholder={placeholderText}
-            placeholderTextColor="grey"
-            onChangeText={(newText: string) => {
-              const newestChar = newText[newText.length - 1];
-              const deletedChar =
-                newText.length === comment.length - 1
-                  ? comment[comment.length - 1]
-                  : undefined;
-              if (newestChar === ' ' || deletedChar === '@') {
-                setIsMentioning(false);
-                setMentionQuery('');
-              }
-              if (newestChar === '@') {
-                setIsMentioning(true);
-              }
-              if (isMentioning) {
-                const match = newText.match(/.*@(.*)$/);
-                if (match) {
-                  setMentionQuery(match[1]);
-                }
-              }
-              setComment(newText);
-            }}
-            multiline={true}
-            ref={ref}>
-            <ParsedText style={styles.text} parse={parsePatterns}>
-              {comment}
-            </ParsedText>
-          </TextInput>
+            value={comment}
+            onChange={setComment}
+            inputRef={ref}
+            partTypes={[
+              {
+                trigger: '@',
+                renderSuggestions: (props) => <TaggTypeahead {...props} />,
+                allowedSpacesCount: 0,
+                isInsertSpaceAfterMention: true,
+                textStyle: {color: TAGG_LIGHT_BLUE},
+              },
+            ]}
+          />
           <View style={styles.submitButton}>
             <TouchableOpacity style={styles.submitButton} onPress={addComment}>
               <UpArrowIcon width={35} height={35} color={'white'} />
diff --git a/src/components/common/TaggTypeahead.tsx b/src/components/common/TaggTypeahead.tsx
index 6239971e..7cd99278 100644
--- a/src/components/common/TaggTypeahead.tsx
+++ b/src/components/common/TaggTypeahead.tsx
@@ -1,33 +1,30 @@
 import React, {Fragment, useEffect, useState} from 'react';
 import {ScrollView, StyleSheet} from 'react-native';
+import {MentionSuggestionsProps} from 'react-native-controlled-mentions';
 import {SEARCH_ENDPOINT_MESSAGES} from '../../constants';
 import {loadSearchResults} from '../../services';
 import {ProfilePreviewType} from '../../types';
 import {SCREEN_WIDTH} from '../../utils';
 import TaggUserRowCell from './TaggUserRowCell';
 
-type TaggTypeaheadProps = {
-  query: string;
-  setSelectedMention: (user: ProfilePreviewType) => void;
-};
-
-const TaggTypeahead: React.FC<TaggTypeaheadProps> = ({
-  query,
-  setSelectedMention,
+const TaggTypeahead: React.FC<MentionSuggestionsProps> = ({
+  keyword,
+  onSuggestionPress,
 }) => {
   const [results, setResults] = useState<ProfilePreviewType[]>([]);
+  const [height, setHeight] = useState(0);
 
   useEffect(() => {
     getQuerySuggested();
-  }, [query]);
+  }, [keyword]);
 
   const getQuerySuggested = async () => {
-    if (query.length < 3) {
+    if (!keyword || keyword.length < 3) {
       setResults([]);
       return;
     }
     const searchResults = await loadSearchResults(
-      `${SEARCH_ENDPOINT_MESSAGES}?query=${query}`,
+      `${SEARCH_ENDPOINT_MESSAGES}?query=${keyword}`,
     );
     if (searchResults && searchResults.users) {
       setResults(searchResults.users);
@@ -39,11 +36,19 @@ const TaggTypeahead: React.FC<TaggTypeaheadProps> = ({
   }
 
   return (
-    <ScrollView style={styles.container} showsVerticalScrollIndicator={false}>
+    <ScrollView
+      style={[styles.container, {top: -(height + 30)}]}
+      showsVerticalScrollIndicator={false}
+      onLayout={(event) => {
+        setHeight(event.nativeEvent.layout.height);
+      }}>
       {results.map((user) => (
         <TaggUserRowCell
           onPress={() => {
-            setSelectedMention(user);
+            onSuggestionPress({
+              id: user.id,
+              name: user.username,
+            });
             setResults([]);
           }}
           user={user}
@@ -57,8 +62,12 @@ const styles = StyleSheet.create({
   container: {
     marginLeft: SCREEN_WIDTH * 0.05,
     width: SCREEN_WIDTH * 0.9,
-    maxHeight: 300,
+    maxHeight: 264,
     borderRadius: 10,
+    backgroundColor: 'white',
+    position: 'absolute',
+    alignSelf: 'center',
+    zIndex: 1,
     borderWidth: 1,
   },
 });
-- 
cgit v1.2.3-70-g09d2


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/AddComment.tsx  | 13 ++-----
 src/components/comments/CommentTile.tsx | 25 ++++++------
 src/utils/comments.tsx                  | 67 +++++++++++++++++++++++++++++++++
 3 files changed, 84 insertions(+), 21 deletions(-)
 create mode 100644 src/utils/comments.tsx

(limited to 'src/components/comments/AddComment.tsx')

diff --git a/src/components/comments/AddComment.tsx b/src/components/comments/AddComment.tsx
index 7576675e..cf5d19ee 100644
--- a/src/components/comments/AddComment.tsx
+++ b/src/components/comments/AddComment.tsx
@@ -16,7 +16,8 @@ import {postComment} from '../../services';
 import {updateReplyPosted} from '../../store/actions';
 import {RootState} from '../../store/rootreducer';
 import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
-import {Avatar, TaggTypeahead} from '../common';
+import {Avatar} from '../common';
+import {mentionPartTypes} from '../../utils/comments';
 
 /**
  * This file provides the add comment view for a user.
@@ -108,15 +109,7 @@ const AddComment: React.FC<AddCommentProps> = ({
             value={comment}
             onChange={setComment}
             inputRef={ref}
-            partTypes={[
-              {
-                trigger: '@',
-                renderSuggestions: (props) => <TaggTypeahead {...props} />,
-                allowedSpacesCount: 0,
-                isInsertSpaceAfterMention: true,
-                textStyle: {color: TAGG_LIGHT_BLUE},
-              },
-            ]}
+            partTypes={mentionPartTypes}
           />
           <View style={styles.submitButton}>
             <TouchableOpacity style={styles.submitButton} onPress={addComment}>
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>
diff --git a/src/utils/comments.tsx b/src/utils/comments.tsx
new file mode 100644
index 00000000..a1f353d6
--- /dev/null
+++ b/src/utils/comments.tsx
@@ -0,0 +1,67 @@
+import React from 'react';
+import {Text} from 'react-native';
+import {
+  isMentionPartType,
+  parseValue,
+  Part,
+  PartType,
+} from 'react-native-controlled-mentions';
+import TaggTypeahead from '../components/common/TaggTypeahead';
+import {TAGG_LIGHT_BLUE} from '../constants';
+
+/**
+ * Part renderer
+ *
+ * https://github.com/dabakovich/react-native-controlled-mentions#rendering-mentioninputs-value
+ *
+ * @param part
+ * @param index
+ */
+const renderPart = (part: Part, index: number) => {
+  // Just plain text
+  if (!part.partType) {
+    return <Text key={index}>{part.text}</Text>;
+  }
+
+  // Mention type part
+  if (isMentionPartType(part.partType)) {
+    return (
+      <Text
+        key={`${index}-${part.data?.trigger}`}
+        style={part.partType.textStyle}
+        onPress={() => console.log('Pressed', part.data)}>
+        {part.text}
+      </Text>
+    );
+  }
+
+  // Other styled part types
+  return (
+    <Text key={`${index}-pattern`} style={part.partType.textStyle}>
+      {part.text}
+    </Text>
+  );
+};
+
+/**
+ * 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 renderValue: React.FC = (value: string, partTypes: PartType[]) => {
+  const {parts} = parseValue(value, partTypes);
+  return <Text>{parts.map(renderPart)}</Text>;
+};
+
+export const mentionPartTypes: PartType[] = [
+  {
+    trigger: '@',
+    renderSuggestions: (props) => <TaggTypeahead {...props} />,
+    allowedSpacesCount: 0,
+    isInsertSpaceAfterMention: true,
+    textStyle: {color: TAGG_LIGHT_BLUE},
+  },
+];
-- 
cgit v1.2.3-70-g09d2


From e67c5d50ebd33967c069134aa0e03111674e1c5d Mon Sep 17 00:00:00 2001
From: Ivan Chen <ivan@tagg.id>
Date: Fri, 30 Apr 2021 18:26:19 -0400
Subject: added mentioning for reply

---
 src/components/comments/AddComment.tsx       | 35 ++++++++++++++++++----------
 src/screens/profile/MomentCommentsScreen.tsx |  7 +++---
 2 files changed, 27 insertions(+), 15 deletions(-)

(limited to 'src/components/comments/AddComment.tsx')

diff --git a/src/components/comments/AddComment.tsx b/src/components/comments/AddComment.tsx
index cf5d19ee..caba68d5 100644
--- a/src/components/comments/AddComment.tsx
+++ b/src/components/comments/AddComment.tsx
@@ -15,6 +15,7 @@ import {TAGG_LIGHT_BLUE} from '../../constants';
 import {postComment} from '../../services';
 import {updateReplyPosted} from '../../store/actions';
 import {RootState} from '../../store/rootreducer';
+import {CommentType} from '../../types';
 import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
 import {Avatar} from '../common';
 import {mentionPartTypes} from '../../utils/comments';
@@ -27,33 +28,36 @@ import {mentionPartTypes} from '../../utils/comments';
 
 export interface AddCommentProps {
   setNewCommentsAvailable: Function;
-  objectId: string;
+  // 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;
   placeholderText: string;
-  isCommentInFocus: boolean;
 }
 
 const AddComment: React.FC<AddCommentProps> = ({
   setNewCommentsAvailable,
-  objectId,
+  commentInReplyTo,
+  momentId,
   placeholderText,
-  isCommentInFocus,
+  isReplying,
 }) => {
   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 addComment = async () => {
     const trimmed = comment.trim();
     if (trimmed === '') {
       return;
     }
-    const postedComment = await postComment(
-      trimmed,
-      objectId,
-      isCommentInFocus,
-    );
+    const postedComment = await postComment(trimmed, objectId, isReplying);
 
     if (postedComment) {
       setComment('');
@@ -61,7 +65,7 @@ const AddComment: React.FC<AddCommentProps> = ({
       //Set new reply posted object
       //This helps us show the latest reply on top
       //Data set is kind of stale but it works
-      if (isCommentInFocus) {
+      if (isReplying) {
         dispatch(
           updateReplyPosted({
             comment_id: postedComment.comment_id,
@@ -73,6 +77,13 @@ const AddComment: React.FC<AddCommentProps> = ({
     }
   };
 
+  useEffect(() => {
+    if (isReplying && commentInReplyTo) {
+      const commenter = commentInReplyTo.commenter;
+      setComment(`@[${commenter.username}](${commenter.id}) `);
+    }
+  }, [isReplying, commentInReplyTo]);
+
   useEffect(() => {
     const showKeyboard = () => setKeyboardVisible(true);
     Keyboard.addListener('keyboardWillShow', showKeyboard);
@@ -87,10 +98,10 @@ const AddComment: React.FC<AddCommentProps> = ({
 
   //If a comment is in Focus, bring the keyboard up so user is able to type in a reply
   useEffect(() => {
-    if (isCommentInFocus) {
+    if (isReplying) {
       ref.current?.focus();
     }
-  }, [isCommentInFocus]);
+  }, [isReplying]);
 
   return (
     <KeyboardAvoidingView
diff --git a/src/screens/profile/MomentCommentsScreen.tsx b/src/screens/profile/MomentCommentsScreen.tsx
index effbbb63..73266a39 100644
--- a/src/screens/profile/MomentCommentsScreen.tsx
+++ b/src/screens/profile/MomentCommentsScreen.tsx
@@ -66,10 +66,11 @@ const MomentCommentsScreen: React.FC<MomentCommentsScreenProps> = ({route}) => {
                 : ADD_COMMENT_TEXT()
             }
             setNewCommentsAvailable={setNewCommentsAvailable}
-            objectId={
-              commentObjectInFocus ? commentObjectInFocus.comment_id : moment_id
+            commentInReplyTo={
+              commentObjectInFocus ? commentObjectInFocus : undefined
             }
-            isCommentInFocus={commentObjectInFocus ? true : false}
+            momentId={commentObjectInFocus ? undefined : moment_id}
+            isReplying={commentObjectInFocus ? true : false}
           />
         </View>
       </SafeAreaView>
-- 
cgit v1.2.3-70-g09d2


From c5cfb608eeb512c3b00c20776b63faf70943b76e Mon Sep 17 00:00:00 2001
From: Ivan Chen <ivan@tagg.id>
Date: Tue, 4 May 2021 15:02:12 -0400
Subject: fixed issue where mention doesn't go away

---
 src/components/comments/AddComment.tsx | 20 +++++++++-----------
 1 file changed, 9 insertions(+), 11 deletions(-)

(limited to 'src/components/comments/AddComment.tsx')

diff --git a/src/components/comments/AddComment.tsx b/src/components/comments/AddComment.tsx
index caba68d5..f9c5669b 100644
--- a/src/components/comments/AddComment.tsx
+++ b/src/components/comments/AddComment.tsx
@@ -43,6 +43,7 @@ const AddComment: React.FC<AddCommentProps> = ({
   placeholderText,
   isReplying,
 }) => {
+  const [inReplyToMention, setInReplyToMention] = useState('');
   const [comment, setComment] = useState('');
   const [keyboardVisible, setKeyboardVisible] = useState(false);
   const {avatar} = useSelector((state: RootState) => state.user);
@@ -77,13 +78,6 @@ const AddComment: React.FC<AddCommentProps> = ({
     }
   };
 
-  useEffect(() => {
-    if (isReplying && commentInReplyTo) {
-      const commenter = commentInReplyTo.commenter;
-      setComment(`@[${commenter.username}](${commenter.id}) `);
-    }
-  }, [isReplying, commentInReplyTo]);
-
   useEffect(() => {
     const showKeyboard = () => setKeyboardVisible(true);
     Keyboard.addListener('keyboardWillShow', showKeyboard);
@@ -96,12 +90,16 @@ const AddComment: React.FC<AddCommentProps> = ({
     return () => Keyboard.removeListener('keyboardWillHide', hideKeyboard);
   }, []);
 
-  //If a comment is in Focus, bring the keyboard up so user is able to type in a reply
   useEffect(() => {
-    if (isReplying) {
+    if (isReplying && commentInReplyTo) {
+      // bring up keyboard
       ref.current?.focus();
+      const commenter = commentInReplyTo.commenter;
+      setInReplyToMention(`@[${commenter.username}](${commenter.id}) `);
+    } else {
+      setInReplyToMention('');
     }
-  }, [isReplying]);
+  }, [isReplying, commentInReplyTo]);
 
   return (
     <KeyboardAvoidingView
@@ -117,7 +115,7 @@ const AddComment: React.FC<AddCommentProps> = ({
           <MentionInput
             containerStyle={styles.text}
             placeholder={placeholderText}
-            value={comment}
+            value={inReplyToMention + comment}
             onChange={setComment}
             inputRef={ref}
             partTypes={mentionPartTypes}
-- 
cgit v1.2.3-70-g09d2


From de9f7b13babb5f9656da797c863064578a3a49c3 Mon Sep 17 00:00:00 2001
From: Ivan Chen <ivan@tagg.id>
Date: Tue, 4 May 2021 17:18:54 -0400
Subject: fixed multiple mentions getting populated

---
 src/components/comments/AddComment.tsx | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

(limited to 'src/components/comments/AddComment.tsx')

diff --git a/src/components/comments/AddComment.tsx b/src/components/comments/AddComment.tsx
index f9c5669b..cce846e5 100644
--- a/src/components/comments/AddComment.tsx
+++ b/src/components/comments/AddComment.tsx
@@ -116,7 +116,12 @@ const AddComment: React.FC<AddCommentProps> = ({
             containerStyle={styles.text}
             placeholder={placeholderText}
             value={inReplyToMention + comment}
-            onChange={setComment}
+            onChange={(newText: string) => {
+              // skipping the `inReplyToMention` text
+              setComment(
+                newText.substring(inReplyToMention.length, newText.length),
+              );
+            }}
             inputRef={ref}
             partTypes={mentionPartTypes}
           />
-- 
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/AddComment.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

