diff options
-rw-r--r-- | src/components/comments/AddComment.tsx | 118 | ||||
-rw-r--r-- | src/components/comments/CommentTextField.tsx | 147 | ||||
-rw-r--r-- | src/components/comments/MentionInputControlled.tsx | 55 | ||||
-rw-r--r-- | src/components/comments/index.ts | 1 | ||||
-rw-r--r-- | src/components/common/TaggTypeahead.tsx | 80 | ||||
-rw-r--r-- | src/components/moments/MomentCommentPreview.tsx | 8 | ||||
-rw-r--r-- | src/screens/profile/CaptionScreen.tsx | 6 | ||||
-rw-r--r-- | src/utils/comments.tsx | 11 |
8 files changed, 306 insertions, 120 deletions
diff --git a/src/components/comments/AddComment.tsx b/src/components/comments/AddComment.tsx index 9667046c..01325475 100644 --- a/src/components/comments/AddComment.tsx +++ b/src/components/comments/AddComment.tsx @@ -7,43 +7,29 @@ import { TextInput, View, } from 'react-native'; -import {TouchableOpacity} from 'react-native-gesture-handler'; -import {useDispatch, useSelector} from 'react-redux'; -import UpArrowIcon from '../../assets/icons/up_arrow.svg'; +import {useDispatch} from 'react-redux'; 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 {CommentThreadType, CommentType} from '../../types'; import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; import {mentionPartTypes} from '../../utils/comments'; -import {Avatar} from '../common'; -import {MentionInputControlled} from './MentionInputControlled'; +import {CommentTextField} from './CommentTextField'; +import MentionInputControlled from './MentionInputControlled'; +import {normalize} from 'react-native-elements'; export interface AddCommentProps { momentId: string; placeholderText: string; - callback?: (message: string) => void; - onFocus?: () => void; - isKeyboardAvoiding?: boolean; - theme?: 'dark' | 'white'; } -const AddComment: React.FC<AddCommentProps> = ({ - momentId, - placeholderText, - callback = (_) => null, - onFocus = () => null, - isKeyboardAvoiding = true, - theme = 'white', -}) => { - const {setShouldUpdateAllComments = () => null, commentTapped} = +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 isReplyingToComment = @@ -61,15 +47,13 @@ const AddComment: React.FC<AddCommentProps> = ({ if (trimmed === '') { return; } - const message = inReplyToMention + trimmed; const postedComment = await postComment( - message, + inReplyToMention + trimmed, objectId, isReplyingToComment || isReplyingToReply, ); if (postedComment) { - callback(message); setComment(''); setInReplyToMention(''); @@ -113,81 +97,64 @@ const AddComment: React.FC<AddCommentProps> = ({ } }, [isReplyingToComment, isReplyingToReply, commentTapped]); - const mainContent = () => ( - <View - style={[ - theme === 'white' ? styles.containerWhite : styles.containerDark, - keyboardVisible && theme !== 'dark' ? styles.whiteBackround : {}, - ]}> - <View style={styles.textContainer}> - <Avatar style={styles.avatar} uri={avatar} /> - <MentionInputControlled - containerStyle={styles.text} - placeholderTextColor={theme === 'dark' ? '#828282' : undefined} - placeholder={placeholderText} - value={inReplyToMention + comment} - onFocus={onFocus} - onChange={(newText: string) => { - // skipping the `inReplyToMention` text - setComment( - newText.substring(inReplyToMention.length, newText.length), - ); - }} - inputRef={ref} - partTypes={mentionPartTypes('blue')} - /> - {(theme === 'white' || (theme === 'dark' && keyboardVisible)) && ( - <View style={styles.submitButton}> - <TouchableOpacity - style={ - comment === '' - ? [styles.submitButton, styles.greyButton] - : styles.submitButton - } - disabled={comment === ''} - onPress={addComment}> - <UpArrowIcon width={35} height={35} color={'white'} /> - </TouchableOpacity> - </View> - )} - </View> - </View> - ); - return isKeyboardAvoiding ? ( + return ( <KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'padding' : 'height'} keyboardVerticalOffset={SCREEN_HEIGHT * 0.1}> - {mainContent()} + <View + style={[ + styles.container, + keyboardVisible ? styles.whiteBackround : {}, + ]}> + <View style={styles.textContainer}> + <MentionInputControlled + containerStyle={styles.text} + placeholder={placeholderText} + value={inReplyToMention + comment} + onChange={(newText: string) => { + // skipping the `inReplyToMention` text + setComment( + newText.substring(inReplyToMention.length, newText.length), + ); + }} + inputRef={ref} + partTypes={mentionPartTypes('blue', 'comment')} + addComment={addComment} + NewText={CommentTextField} + /> + </View> + </View> </KeyboardAvoidingView> - ) : ( - mainContent() ); }; const styles = StyleSheet.create({ - containerDark: { - alignItems: 'center', - width: SCREEN_WIDTH, - }, - containerWhite: { + container: { backgroundColor: '#f7f7f7', alignItems: 'center', + justifyContent: 'center', width: SCREEN_WIDTH, }, textContainer: { width: '95%', - flexDirection: 'row', + // flexDirection: 'row', backgroundColor: '#e8e8e8', alignItems: 'center', + // alignSelf: 'center', justifyContent: 'space-between', + // justifyContent: 'center', margin: '3%', borderRadius: 25, + // borderWidth: 1, + // flex: 1, + height: normalize(45), }, text: { flex: 1, padding: '1%', - marginHorizontal: '1%', + // marginHorizontal: '1%', maxHeight: 100, + // borderWidth: 1, }, avatar: { height: 35, @@ -209,9 +176,6 @@ const styles = StyleSheet.create({ marginVertical: '2%', alignSelf: 'flex-end', }, - greyButton: { - backgroundColor: 'grey', - }, whiteBackround: { backgroundColor: '#fff', }, diff --git a/src/components/comments/CommentTextField.tsx b/src/components/comments/CommentTextField.tsx new file mode 100644 index 00000000..3e97449c --- /dev/null +++ b/src/components/comments/CommentTextField.tsx @@ -0,0 +1,147 @@ +import React, {FC, ReactFragment} from 'react'; +import { + NativeSyntheticEvent, + StyleSheet, + StyleProp, + Text, + TextInput, + TextInputSelectionChangeEventData, + TouchableOpacity, + View, + ViewStyle, +} from 'react-native'; +import {useSelector} from 'react-redux'; +import {TAGG_LIGHT_BLUE} from '../../constants'; +import {RootState} from '../../store/rootReducer'; +import { + Part, + PartType, + MentionPartType, +} from 'react-native-controlled-mentions/dist/types'; +import { + defaultMentionTextStyle, + isMentionPartType, +} from 'react-native-controlled-mentions/dist/utils'; +import {Avatar} from '../common'; +import {normalize} from 'react-native-elements'; + +import UpArrowIcon from '../../assets/icons/up_arrow.svg'; + +type CommentTextFieldProps = { + containerStyle: StyleProp<ViewStyle>; + validateInput: any; + keyboardText: string; + partTypes: PartType[]; + renderMentionSuggestions: (mentionType: MentionPartType) => ReactFragment; + handleTextInputRef: (ref: TextInput) => null; + onChangeInput: (changedText: string) => null; + handleSelectionChange: ( + event: NativeSyntheticEvent<TextInputSelectionChangeEventData>, + ) => null; + parts: Part[]; + addComment: () => any; +}; + +const CommentTextField: FC<CommentTextFieldProps> = ({ + containerStyle, + validateInput, + keyboardText, + partTypes, + renderMentionSuggestions, + handleTextInputRef, + onChangeInput, + handleSelectionChange, + parts, + addComment, + ...textInputProps +}) => { + const {avatar} = useSelector((state: RootState) => state.user); + + return ( + <View style={containerStyle}> + {validateInput(keyboardText) + ? ( + partTypes.filter( + (one) => + isMentionPartType(one) && + one.renderSuggestions != null && + !one.isBottomMentionSuggestionsRender, + ) as MentionPartType[] + ).map(renderMentionSuggestions) + : null} + + <View style={styles.containerStyle}> + <Avatar style={styles.avatar} uri={avatar} /> + <TextInput + multiline + {...textInputProps} + ref={handleTextInputRef} + onChangeText={onChangeInput} + onSelectionChange={handleSelectionChange} + style={styles.text}> + <Text> + {parts.map(({text, partType, data}, index) => + partType ? ( + <Text + key={`${index}-${data?.trigger ?? 'pattern'}`} + style={partType.textStyle ?? defaultMentionTextStyle}> + {text} + </Text> + ) : ( + <Text key={index}>{text}</Text> + ), + )} + </Text> + </TextInput> + <View style={styles.submitButton}> + <TouchableOpacity style={styles.submitButton} onPress={addComment}> + <UpArrowIcon width={35} height={35} color={'white'} /> + </TouchableOpacity> + </View> + </View> + + {validateInput(keyboardText) + ? ( + partTypes.filter( + (one) => + isMentionPartType(one) && + one.renderSuggestions != null && + one.isBottomMentionSuggestionsRender, + ) as MentionPartType[] + ).map(renderMentionSuggestions) + : null} + </View> + ); +}; + +const styles = StyleSheet.create({ + avatar: { + height: 35, + width: 35, + borderRadius: 30, + marginRight: 10, + marginLeft: '3%', + marginVertical: '2%', + }, + containerStyle: { + flexDirection: 'row', + alignSelf: 'center', + alignItems: 'center', + justifyContent: 'center', + height: normalize(40), + }, + submitButton: { + height: 35, + width: 35, + backgroundColor: TAGG_LIGHT_BLUE, + borderRadius: 999, + justifyContent: 'center', + alignItems: 'center', + marginRight: '3%', + marginVertical: '2%', + alignSelf: 'flex-end', + }, + text: {flex: 1}, +}); + +export {CommentTextField}; diff --git a/src/components/comments/MentionInputControlled.tsx b/src/components/comments/MentionInputControlled.tsx index 2fd2b41d..de52d1c1 100644 --- a/src/components/comments/MentionInputControlled.tsx +++ b/src/components/comments/MentionInputControlled.tsx @@ -1,13 +1,23 @@ -import React, {FC, MutableRefObject, useMemo, useRef, useState} from 'react'; +import React, { + FC, + MutableRefObject, + Ref, + useMemo, + useRef, + useState, +} from 'react'; import { NativeSyntheticEvent, + StyleProp, Text, TextInput, + TextInputProps, TextInputSelectionChangeEventData, View, + ViewStyle, } from 'react-native'; import { - MentionInputProps, + PatternPartType, MentionPartType, Suggestion, } from 'react-native-controlled-mentions/dist/types'; @@ -20,7 +30,24 @@ import { parseValue, } from 'react-native-controlled-mentions/dist/utils'; -const MentionInputControlled: FC<MentionInputProps> = ({ +type PartType = MentionPartType | PatternPartType; + +type MentionInputControlledProps = Omit<TextInputProps, 'onChange'> & { + value: string; + onChange: (value: string) => any; + + partTypes?: PartType[]; + + inputRef?: Ref<TextInput>; + + containerStyle?: StyleProp<ViewStyle>; + + addComment?: () => any | null; + + NewText?: FC<any>; +}; + +const MentionInputControlled: FC<MentionInputControlledProps> = ({ value, onChange, @@ -32,6 +59,10 @@ const MentionInputControlled: FC<MentionInputProps> = ({ onSelectionChange, + addComment, + + NewText, + ...textInputProps }) => { const textInput = useRef<TextInput | null>(null); @@ -147,7 +178,21 @@ const MentionInputControlled: FC<MentionInputProps> = ({ return validRegex().test(testString); }; - return ( + return NewText ? ( + <NewText + {...textInputProps} + containerStyle={containerStyle} + validateInput={validateInput} + keyboardText={keyboardText} + partTypes={partTypes} + renderMentionSuggestions={renderMentionSuggestions} + handleTextInputRef={handleTextInputRef} + onChangeInput={onChangeInput} + handleSelectionChange={handleSelectionChange} + parts={parts} + addComment={addComment} + /> + ) : ( <View style={containerStyle}> {validateInput(keyboardText) ? ( @@ -195,4 +240,4 @@ const MentionInputControlled: FC<MentionInputProps> = ({ ); }; -export {MentionInputControlled}; +export default MentionInputControlled; diff --git a/src/components/comments/index.ts b/src/components/comments/index.ts index ebd93844..77334cb3 100644 --- a/src/components/comments/index.ts +++ b/src/components/comments/index.ts @@ -1,2 +1,3 @@ export {default as CommentTile} from './CommentTile'; export {default as AddComment} from './AddComment'; +export {default as MentionInputControlled} from './MentionInputControlled'; diff --git a/src/components/common/TaggTypeahead.tsx b/src/components/common/TaggTypeahead.tsx index 747e0bb5..6b3df559 100644 --- a/src/components/common/TaggTypeahead.tsx +++ b/src/components/common/TaggTypeahead.tsx @@ -1,21 +1,35 @@ import React, {Fragment, useEffect, useState} from 'react'; -import {ScrollView, StyleSheet} from 'react-native'; -import {MentionSuggestionsProps} from 'react-native-controlled-mentions'; +import {ScrollView, StyleSheet, View} from 'react-native'; +import {Suggestion} from 'react-native-controlled-mentions'; import {useSelector} from 'react-redux'; import {SEARCH_ENDPOINT_MESSAGES} from '../../constants'; import {loadSearchResults} from '../../services'; import {RootState} from '../../store/rootReducer'; import {ProfilePreviewType} from '../../types'; -import {SCREEN_WIDTH, shuffle} from '../../utils'; +import {SCREEN_HEIGHT, SCREEN_WIDTH, shuffle} from '../../utils'; import TaggUserRowCell from './TaggUserRowCell'; -const TaggTypeahead: React.FC<MentionSuggestionsProps> = ({ +type TaggTypeaheadProps = { + keyword: string | undefined; + component: string | undefined; + onSuggestionPress: (suggestion: Suggestion) => void; +}; + +const TaggTypeahead: React.FC<TaggTypeaheadProps> = ({ keyword, + component, onSuggestionPress, }) => { const {friends} = useSelector((state: RootState) => state.friends); const [results, setResults] = useState<ProfilePreviewType[]>([]); const [height, setHeight] = useState(0); + const [margin, setMargin] = useState(0); + + useEffect(() => { + if (component === 'comment') { + setMargin(-10); + } + }, [component]); useEffect(() => { if (keyword === '') { @@ -42,40 +56,50 @@ const TaggTypeahead: React.FC<MentionSuggestionsProps> = ({ } return ( - <ScrollView - style={[styles.container, {top: -(height + 30)}]} - showsVerticalScrollIndicator={false} - onLayout={(event) => { - setHeight(event.nativeEvent.layout.height); - }} - keyboardShouldPersistTaps={'always'}> - {results.map((user) => ( - <TaggUserRowCell - onPress={() => { - setResults([]); - onSuggestionPress({ - id: user.id, - name: user.username, - }); - }} - user={user} - /> - ))} - </ScrollView> + <View> + <View style={styles.overlay} /> + <ScrollView + style={[styles.container, {top: -height, margin: margin}]} + showsVerticalScrollIndicator={false} + onLayout={(event) => { + setHeight(event.nativeEvent.layout.height); + }} + keyboardShouldPersistTaps={'always'}> + {results.map((user) => ( + <TaggUserRowCell + onPress={() => { + setResults([]); + onSuggestionPress({ + id: user.id, + name: user.username, + }); + }} + user={user} + /> + ))} + </ScrollView> + </View> ); }; const styles = StyleSheet.create({ container: { - marginLeft: SCREEN_WIDTH * 0.05, - width: SCREEN_WIDTH * 0.9, + width: SCREEN_WIDTH, maxHeight: 264, - borderRadius: 10, backgroundColor: 'white', position: 'absolute', alignSelf: 'center', zIndex: 1, - borderWidth: 1, + }, + overlay: { + width: SCREEN_WIDTH, + height: SCREEN_HEIGHT, + backgroundColor: 'gray', + opacity: 0.4, + position: 'absolute', + alignSelf: 'center', + bottom: 10, + zIndex: -1, }, }); diff --git a/src/components/moments/MomentCommentPreview.tsx b/src/components/moments/MomentCommentPreview.tsx index e53ed258..f6b9d75b 100644 --- a/src/components/moments/MomentCommentPreview.tsx +++ b/src/components/moments/MomentCommentPreview.tsx @@ -9,7 +9,7 @@ import {mentionPartTypes, renderTextWithMentions} from '../../utils/comments'; interface MomentCommentPreviewProps { momentId: string; - commentsCount: number; + commentsCount: number | null; commentPreview: MomentCommentPreviewType | null; screenType: ScreenType; } @@ -23,7 +23,9 @@ const MomentCommentPreview: React.FC<MomentCommentPreviewProps> = ({ const navigation = useNavigation(); const state = useStore().getState(); const commentCountText = - commentsCount === 0 ? 'No Comments' : commentsCount + ' comments'; + !commentsCount || commentsCount === 0 + ? 'No Comments' + : commentsCount + ' comments'; return ( <TouchableOpacity @@ -35,7 +37,7 @@ const MomentCommentPreview: React.FC<MomentCommentPreviewProps> = ({ }) }> <Text style={styles.whiteBold}>{commentCountText}</Text> - {commentPreview !== null && ( + {commentPreview && ( <View style={styles.previewContainer}> <Image source={{ diff --git a/src/screens/profile/CaptionScreen.tsx b/src/screens/profile/CaptionScreen.tsx index 9e1b4674..253346d5 100644 --- a/src/screens/profile/CaptionScreen.tsx +++ b/src/screens/profile/CaptionScreen.tsx @@ -13,7 +13,7 @@ import { TouchableWithoutFeedback, View, } from 'react-native'; -import {MentionInput} from 'react-native-controlled-mentions'; +import {MentionInputControlled} from '../../components'; import {Button, normalize} from 'react-native-elements'; import {useDispatch, useSelector} from 'react-redux'; import FrontArrow from '../../assets/icons/front-arrow.svg'; @@ -199,13 +199,13 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { source={{uri: moment ? moment.moment_url : image?.path}} resizeMode={'cover'} /> - <MentionInput + <MentionInputControlled containerStyle={styles.text} placeholder="Write something....." placeholderTextColor="gray" value={caption} onChange={setCaption} - partTypes={mentionPartTypes('blue')} + partTypes={mentionPartTypes('blue', 'caption')} /> <TouchableOpacity onPress={() => diff --git a/src/utils/comments.tsx b/src/utils/comments.tsx index 910b44e7..28879622 100644 --- a/src/utils/comments.tsx +++ b/src/utils/comments.tsx @@ -79,13 +79,16 @@ export const renderTextWithMentions: React.FC<RenderProps> = ({ ); }; -export const mentionPartTypes: (theme: 'blue' | 'white') => PartType[] = ( - theme, -) => { +export const mentionPartTypes: ( + theme: 'blue' | 'white', + component: 'caption' | 'comment', +) => PartType[] = (theme, component) => { return [ { trigger: '@', - renderSuggestions: (props) => <TaggTypeahead {...props} />, + renderSuggestions: (props) => ( + <TaggTypeahead component={component} {...props} /> + ), allowedSpacesCount: 0, isInsertSpaceAfterMention: true, textStyle: _textStyle(theme), |