aboutsummaryrefslogtreecommitdiff
path: root/src/components/comments
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/comments')
-rw-r--r--src/components/comments/AddComment.tsx44
-rw-r--r--src/components/comments/CommentTextField.tsx164
-rw-r--r--src/components/comments/CommentsCount.tsx48
-rw-r--r--src/components/comments/MentionInputControlled.tsx70
-rw-r--r--src/components/comments/ZoomInCropper.tsx201
-rw-r--r--src/components/comments/index.ts1
6 files changed, 492 insertions, 36 deletions
diff --git a/src/components/comments/AddComment.tsx b/src/components/comments/AddComment.tsx
index 9667046c..8a4ec082 100644
--- a/src/components/comments/AddComment.tsx
+++ b/src/components/comments/AddComment.tsx
@@ -7,19 +7,16 @@ 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 {SCREEN_HEIGHT, SCREEN_WIDTH, normalize} from '../../utils';
import {mentionPartTypes} from '../../utils/comments';
-import {Avatar} from '../common';
-import {MentionInputControlled} from './MentionInputControlled';
+import {CommentTextField} from './CommentTextField';
+import MentionInputControlled from './MentionInputControlled';
export interface AddCommentProps {
momentId: string;
@@ -38,12 +35,11 @@ const AddComment: React.FC<AddCommentProps> = ({
isKeyboardAvoiding = true,
theme = 'white',
}) => {
- const {setShouldUpdateAllComments = () => null, commentTapped} =
+ 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 =
@@ -120,7 +116,6 @@ const AddComment: React.FC<AddCommentProps> = ({
keyboardVisible && theme !== 'dark' ? styles.whiteBackround : {},
]}>
<View style={styles.textContainer}>
- <Avatar style={styles.avatar} uri={avatar} />
<MentionInputControlled
containerStyle={styles.text}
placeholderTextColor={theme === 'dark' ? '#828282' : undefined}
@@ -134,25 +129,17 @@ const AddComment: React.FC<AddCommentProps> = ({
);
}}
inputRef={ref}
- partTypes={mentionPartTypes('blue')}
+ partTypes={mentionPartTypes('blue', 'comment')}
+ addComment={addComment}
+ NewText={CommentTextField}
+ theme={theme}
+ keyboardVisible={keyboardVisible}
+ comment={comment}
/>
- {(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 ? (
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
@@ -176,17 +163,15 @@ const styles = StyleSheet.create({
},
textContainer: {
width: '95%',
- flexDirection: 'row',
backgroundColor: '#e8e8e8',
alignItems: 'center',
justifyContent: 'space-between',
margin: '3%',
borderRadius: 25,
+ height: normalize(45),
},
text: {
flex: 1,
- padding: '1%',
- marginHorizontal: '1%',
maxHeight: 100,
},
avatar: {
@@ -209,9 +194,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..6e92329c
--- /dev/null
+++ b/src/components/comments/CommentTextField.tsx
@@ -0,0 +1,164 @@
+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 '../../utils';
+
+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;
+ theme?: 'dark' | 'white';
+ keyboardVisible?: boolean;
+ comment?: string;
+};
+
+const CommentTextField: FC<CommentTextFieldProps> = ({
+ containerStyle,
+ validateInput,
+ keyboardText,
+ partTypes,
+ renderMentionSuggestions,
+ handleTextInputRef,
+ onChangeInput,
+ handleSelectionChange,
+ parts,
+ addComment,
+ theme = 'white',
+ keyboardVisible = true,
+ comment = '',
+ ...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>
+ {(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>
+
+ {validateInput(keyboardText) &&
+ (
+ partTypes.filter(
+ (one) =>
+ isMentionPartType(one) &&
+ one.renderSuggestions != null &&
+ one.isBottomMentionSuggestionsRender,
+ ) as MentionPartType[]
+ ).map(renderMentionSuggestions)}
+ </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(45),
+ },
+ greyButton: {
+ backgroundColor: 'grey',
+ },
+ 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/CommentsCount.tsx b/src/components/comments/CommentsCount.tsx
new file mode 100644
index 00000000..90514193
--- /dev/null
+++ b/src/components/comments/CommentsCount.tsx
@@ -0,0 +1,48 @@
+import {useNavigation} from '@react-navigation/core';
+import React from 'react';
+import {StyleSheet, Text} from 'react-native';
+import {TouchableOpacity} from 'react-native-gesture-handler';
+import CommentsIcon from '../../assets/icons/moment-comment-icon.svg';
+import {MomentPostType, ScreenType} from '../../types';
+import {normalize} from '../../utils';
+
+interface CommentsCountProps {
+ moment: MomentPostType;
+ screenType: ScreenType;
+}
+
+const CommentsCount: React.FC<CommentsCountProps> = ({moment, screenType}) => {
+ const navigation = useNavigation();
+ return (
+ <TouchableOpacity
+ style={styles.countContainer}
+ onPress={() =>
+ navigation.navigate('MomentCommentsScreen', {
+ moment_id: moment.moment_id,
+ screenType,
+ })
+ }>
+ <CommentsIcon width={25} height={25} />
+ <Text style={styles.count}>{moment.comments_count}</Text>
+ </TouchableOpacity>
+ );
+};
+
+const styles = StyleSheet.create({
+ countContainer: {
+ minWidth: 50,
+ flexDirection: 'column',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ count: {
+ fontWeight: '500',
+ fontSize: normalize(11),
+ lineHeight: normalize(13),
+ letterSpacing: normalize(0.05),
+ textAlign: 'center',
+ color: 'white',
+ marginTop: normalize(5),
+ },
+});
+export default CommentsCount;
diff --git a/src/components/comments/MentionInputControlled.tsx b/src/components/comments/MentionInputControlled.tsx
index 2fd2b41d..0965e318 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,30 @@ 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>;
+
+ theme?: 'dark' | 'white';
+
+ keyboardVisible?: boolean;
+
+ comment?: string;
+};
+
+const MentionInputControlled: FC<MentionInputControlledProps> = ({
value,
onChange,
@@ -32,6 +65,16 @@ const MentionInputControlled: FC<MentionInputProps> = ({
onSelectionChange,
+ addComment,
+
+ NewText,
+
+ theme = 'white',
+
+ keyboardVisible = true,
+
+ comment = '',
+
...textInputProps
}) => {
const textInput = useRef<TextInput | null>(null);
@@ -147,7 +190,24 @@ 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}
+ theme={theme}
+ keyboardVisible={keyboardVisible}
+ comment={comment}
+ />
+ ) : (
<View style={containerStyle}>
{validateInput(keyboardText)
? (
@@ -195,4 +255,4 @@ const MentionInputControlled: FC<MentionInputProps> = ({
);
};
-export {MentionInputControlled};
+export default MentionInputControlled;
diff --git a/src/components/comments/ZoomInCropper.tsx b/src/components/comments/ZoomInCropper.tsx
new file mode 100644
index 00000000..bca4e599
--- /dev/null
+++ b/src/components/comments/ZoomInCropper.tsx
@@ -0,0 +1,201 @@
+import {RouteProp} from '@react-navigation/core';
+import {useFocusEffect} from '@react-navigation/native';
+import {StackNavigationProp} from '@react-navigation/stack';
+import {default as React, useCallback, useEffect, useState} from 'react';
+import {Image, StyleSheet, TouchableOpacity} from 'react-native';
+import {normalize} from 'react-native-elements';
+import ImageZoom, {IOnMove} from 'react-native-image-pan-zoom';
+import PhotoManipulator from 'react-native-photo-manipulator';
+import CloseIcon from '../../assets/ionicons/close-outline.svg';
+import {MainStackParams} from '../../routes';
+import {HeaderHeight, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
+import {TaggSquareButton} from '../common';
+
+type ZoomInCropperRouteProps = RouteProp<MainStackParams, 'ZoomInCropper'>;
+type ZoomInCropperNavigationProps = StackNavigationProp<
+ MainStackParams,
+ 'ZoomInCropper'
+>;
+interface ZoomInCropperProps {
+ route: ZoomInCropperRouteProps;
+ navigation: ZoomInCropperNavigationProps;
+}
+
+export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({
+ route,
+ navigation,
+}) => {
+ const {screenType, title, media} = route.params;
+ const [aspectRatio, setAspectRatio] = useState<number>(1);
+
+ // Stores the coordinates of the cropped image
+ const [x0, setX0] = useState<number>();
+ const [x1, setX1] = useState<number>();
+ const [y0, setY0] = useState<number>();
+ const [y1, setY1] = useState<number>();
+
+ // Removes bottom navigation bar on current screen and add it back when navigating away
+ useFocusEffect(
+ useCallback(() => {
+ navigation.dangerouslyGetParent()?.setOptions({
+ tabBarVisible: false,
+ });
+ return () => {
+ navigation.dangerouslyGetParent()?.setOptions({
+ tabBarVisible: true,
+ });
+ };
+ }, [navigation]),
+ );
+
+ // Setting original aspect ratio of image
+ useEffect(() => {
+ if (media.uri) {
+ Image.getSize(
+ media.uri,
+ (w, h) => {
+ setAspectRatio(w / h);
+ },
+ (err) => console.log(err),
+ );
+ }
+ }, []);
+
+ // Crops original image based of (x0, y0) and (x1, y1) coordinates
+ const handleNext = () => {
+ if (
+ x0 !== undefined &&
+ x1 !== undefined &&
+ y0 !== undefined &&
+ y1 !== undefined
+ ) {
+ PhotoManipulator.crop(media.uri, {
+ x: x0,
+ y: y1,
+ width: Math.abs(x0 - x1),
+ height: Math.abs(y0 - y1),
+ })
+ .then((croppedURL) => {
+ navigation.navigate('CaptionScreen', {
+ screenType,
+ title: title,
+ media: {
+ filename: media.filename,
+ uri: croppedURL,
+ isVideo: false,
+ },
+ });
+ })
+ .catch((err) => console.log('err: ', err));
+ } else if (
+ x0 === undefined &&
+ x1 === undefined &&
+ y0 === undefined &&
+ y1 === undefined
+ ) {
+ navigation.navigate('CaptionScreen', {
+ screenType,
+ title: title,
+ media,
+ });
+ }
+ };
+
+ /* Records (x0, y0) and (x1, y1) coordinates used later for cropping,
+ * based on(x, y) - the center of the image and scale of zoom
+ */
+ const onMove = (position: IOnMove) => {
+ Image.getSize(
+ media.uri,
+ (w, h) => {
+ const x = position.positionX;
+ const y = position.positionY;
+ const scale = position.scale;
+ const screen_ratio = SCREEN_HEIGHT / SCREEN_WIDTH;
+ let tempx0 = w / 2 - x * (w / SCREEN_WIDTH) - w / 2 / scale;
+ let tempx1 = w / 2 - x * (w / SCREEN_WIDTH) + w / 2 / scale;
+ if (tempx0 < 0) {
+ tempx0 = 0;
+ }
+ if (tempx1 > w) {
+ tempx1 = w;
+ }
+ const x_distance = Math.abs(tempx1 - tempx0);
+ const y_distance = screen_ratio * x_distance;
+ let tempy0 = h / 2 - y * (h / SCREEN_HEIGHT) + y_distance / 2;
+ let tempy1 = h / 2 - y * (h / SCREEN_HEIGHT) - y_distance / 2;
+ if (tempy0 > h) {
+ tempy0 = h;
+ }
+ if (tempy1 < 0) {
+ tempy1 = 0;
+ }
+ setX0(tempx0);
+ setX1(tempx1);
+ setY0(tempy0);
+ setY1(tempy1);
+ },
+ (err) => console.log(err),
+ );
+ };
+
+ return (
+ <>
+ <TouchableOpacity
+ style={styles.closeButton}
+ onPress={() => navigation.goBack()}>
+ <CloseIcon height={25} width={25} color={'white'} />
+ </TouchableOpacity>
+ <ImageZoom
+ style={styles.zoomView}
+ cropWidth={SCREEN_WIDTH}
+ cropHeight={SCREEN_HEIGHT}
+ imageWidth={SCREEN_WIDTH}
+ imageHeight={SCREEN_WIDTH / aspectRatio}
+ onMove={onMove}>
+ <Image
+ style={{width: SCREEN_WIDTH, height: SCREEN_WIDTH / aspectRatio}}
+ source={{
+ uri: media.uri,
+ }}
+ />
+ </ImageZoom>
+ <TaggSquareButton
+ onPress={handleNext}
+ title={'Next'}
+ buttonStyle={'normal'}
+ buttonColor={'blue'}
+ labelColor={'white'}
+ style={styles.button}
+ labelStyle={styles.buttonLabel}
+ />
+ </>
+ );
+};
+
+const styles = StyleSheet.create({
+ closeButton: {
+ position: 'absolute',
+ top: 0,
+ paddingTop: HeaderHeight,
+ zIndex: 1,
+ marginLeft: '5%',
+ },
+ button: {
+ zIndex: 1,
+ position: 'absolute',
+ bottom: normalize(20),
+ right: normalize(15),
+ width: normalize(108),
+ height: normalize(25),
+ borderRadius: 10,
+ },
+ buttonLabel: {
+ fontWeight: '700',
+ fontSize: normalize(15),
+ lineHeight: normalize(17.8),
+ letterSpacing: normalize(1.3),
+ textAlign: 'center',
+ },
+ zoomView: {backgroundColor: 'black'},
+});
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';