aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Chen <ivan@tagg.id>2021-06-25 20:58:56 -0400
committerGitHub <noreply@github.com>2021-06-25 20:58:56 -0400
commit5480267b285812c094246bb941c6deaf83f53ff5 (patch)
tree17f2e23576c000bcc90d840d14b8abc3bb9bec24
parent981051448fee6197544383e535fea7a72827d41d (diff)
parentdcf45600b6e2be7820ed2d8c0f44603624f1e719 (diff)
Merge pull request #475 from IvanIFChen/tma948-video-playback
[TMA-948] Viewing Videos
-rw-r--r--ios/Podfile2
-rw-r--r--ios/Podfile.lock12
-rw-r--r--package.json4
-rw-r--r--src/components/comments/AddComment.tsx65
-rw-r--r--src/components/comments/CommentTextField.tsx157
-rw-r--r--src/components/comments/CommentsCount.tsx53
-rw-r--r--src/components/comments/MentionInputControlled.tsx70
-rw-r--r--src/components/comments/ZoomInCropper.tsx201
-rw-r--r--src/components/comments/index.ts1
-rw-r--r--src/components/common/MomentTags.tsx42
-rw-r--r--src/components/common/TaggTypeahead.tsx74
-rw-r--r--src/components/moments/IndividualMomentTitleBar.tsx58
-rw-r--r--src/components/moments/Moment.tsx22
-rw-r--r--src/components/moments/MomentCommentPreview.tsx8
-rw-r--r--src/components/moments/MomentPost.tsx396
-rw-r--r--src/components/moments/MomentPostHeader.tsx116
-rw-r--r--src/components/moments/index.ts2
-rw-r--r--src/components/moments/legacy/MomentPostContent.tsx (renamed from src/components/moments/MomentPostContent.tsx)26
-rw-r--r--src/components/profile/MomentMoreInfoDrawer.tsx8
-rw-r--r--src/components/profile/PublicProfile.tsx4
-rw-r--r--src/components/taggs/TaggDraggable.tsx4
-rw-r--r--src/constants/strings.ts2
-rw-r--r--src/routes/main/MainStackNavigator.tsx5
-rw-r--r--src/routes/main/MainStackScreen.tsx9
-rw-r--r--src/screens/moments/TagFriendsScreen.tsx83
-rw-r--r--src/screens/profile/CaptionScreen.tsx8
-rw-r--r--src/screens/profile/IndividualMoment.tsx118
-rw-r--r--src/screens/profile/MomentCommentsScreen.tsx5
-rw-r--r--src/services/MomentService.ts1
-rw-r--r--src/utils/comments.tsx11
-rw-r--r--yarn.lock42
31 files changed, 1156 insertions, 453 deletions
diff --git a/ios/Podfile b/ios/Podfile
index 4eca4100..d8324a6d 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -33,4 +33,4 @@ end
# add the Firebase pod for Google Analytics
pod 'Firebase/Analytics'
# add pods for any other desired Firebase products
-# https://firebase.google.com/docs/ios/setup#available-pods \ No newline at end of file
+# https://firebase.google.com/docs/ios/setup#available-pods
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 86339554..15ec026f 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -343,6 +343,9 @@ PODS:
- React-Core
- react-native-netinfo (6.0.0):
- React-Core
+ - react-native-photo-manipulator (1.2.4):
+ - React
+ - WCPhotoManipulator (~> 2.0.4)
- react-native-safe-area-context (3.2.0):
- React-Core
- react-native-splash-screen (3.2.0):
@@ -486,6 +489,7 @@ PODS:
- RNVectorIcons (7.1.0):
- React
- TOCropViewController (2.6.0)
+ - WCPhotoManipulator (2.0.4)
- Yoga (1.14.0)
- YogaKit (1.18.1):
- Yoga (~> 1.14)
@@ -537,6 +541,7 @@ DEPENDENCIES:
- react-native-image-picker (from `../node_modules/react-native-image-picker`)
- react-native-image-resizer (from `../node_modules/react-native-image-resizer`)
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
+ - react-native-photo-manipulator (from `../node_modules/react-native-photo-manipulator`)
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- react-native-splash-screen (from `../node_modules/react-native-splash-screen`)
- react-native-video (from `../node_modules/react-native-video`)
@@ -595,6 +600,7 @@ SPEC REPOS:
- OpenSSL-Universal
- PromisesObjC
- TOCropViewController
+ - WCPhotoManipulator
- YogaKit
EXTERNAL SOURCES:
@@ -646,6 +652,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-image-resizer"
react-native-netinfo:
:path: "../node_modules/@react-native-community/netinfo"
+ react-native-photo-manipulator:
+ :path: "../node_modules/react-native-photo-manipulator"
react-native-safe-area-context:
:path: "../node_modules/react-native-safe-area-context"
react-native-splash-screen:
@@ -757,6 +765,7 @@ SPEC CHECKSUMS:
react-native-image-picker: c07b072faa83f3480b473a15ea3c19cc39b3d6fa
react-native-image-resizer: d9fb629a867335bdc13230ac2a58702bb8c8828f
react-native-netinfo: e849fc21ca2f4128a5726c801a82fc6f4a6db50d
+ react-native-photo-manipulator: e44c14a28bf7c9b7657a0e0ac79327c1a4d8fe2c
react-native-safe-area-context: f0906bf8bc9835ac9a9d3f97e8bde2a997d8da79
react-native-splash-screen: 200d11d188e2e78cea3ad319964f6142b6384865
react-native-video: 0bb76b6d6b77da3009611586c7dbf817b947f30e
@@ -788,9 +797,10 @@ SPEC CHECKSUMS:
RNSVG: 551acb6562324b1d52a4e0758f7ca0ec234e278f
RNVectorIcons: bc69e6a278b14842063605de32bec61f0b251a59
TOCropViewController: 3105367e808b7d3d886a74ff59bf4804e7d3ab38
+ WCPhotoManipulator: 45b7be19b75c9edf6d2b44f2f61dbc3673862a8f
Yoga: 7d13633d129fd179e01b8953d38d47be90db185a
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
-PODFILE CHECKSUM: e24412577971b52c81c348785bf01cb915155d6d
+PODFILE CHECKSUM: 506904e1c9d422356d8a7be45ae9115719e4d7be
COCOAPODS: 1.10.1
diff --git a/package.json b/package.json
index 399a40ea..09ed6fc5 100644
--- a/package.json
+++ b/package.json
@@ -47,9 +47,11 @@
"react-native-hyperlink": "^0.0.19",
"react-native-image-crop-picker": "^0.36.0",
"react-native-image-picker": "^4.0.4",
+ "react-native-image-pan-zoom": "^2.1.12",
"react-native-image-resizer": "^1.4.4",
"react-native-inappbrowser-reborn": "^3.5.0",
"react-native-linear-gradient": "^2.5.6",
+ "react-native-photo-manipulator": "^1.2.4",
"react-native-picker-select": "^7.0.0",
"react-native-push-notifications": "^3.0.10",
"react-native-reanimated": "2.0.0-rc.0",
@@ -108,4 +110,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 9667046c..33707d94 100644
--- a/src/components/comments/AddComment.tsx
+++ b/src/components/comments/AddComment.tsx
@@ -7,19 +7,15 @@ 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 {TAGG_LIGHT_BLUE} from '../../constants';
+import {useDispatch} from 'react-redux';
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 {normalize, 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';
export interface AddCommentProps {
momentId: string;
@@ -38,12 +34,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 +115,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 +128,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,42 +162,17 @@ 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: {
- height: 35,
- width: 35,
- borderRadius: 30,
- marginRight: 10,
- marginLeft: '3%',
- marginVertical: '2%',
- alignSelf: 'flex-end',
- },
- submitButton: {
- height: 35,
- width: 35,
- backgroundColor: TAGG_LIGHT_BLUE,
- borderRadius: 999,
- justifyContent: 'center',
- alignItems: 'center',
- marginRight: '3%',
- 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..6d86eb3f
--- /dev/null
+++ b/src/components/comments/CommentTextField.tsx
@@ -0,0 +1,157 @@
+import React, {FC, ReactFragment} from 'react';
+import {
+ NativeSyntheticEvent,
+ StyleProp,
+ StyleSheet,
+ Text,
+ TextInput,
+ TextInputSelectionChangeEventData,
+ TouchableOpacity,
+ View,
+ ViewStyle,
+} from 'react-native';
+import {
+ MentionPartType,
+ Part,
+ PartType,
+} from 'react-native-controlled-mentions/dist/types';
+import {
+ defaultMentionTextStyle,
+ isMentionPartType,
+} from 'react-native-controlled-mentions/dist/utils';
+import {useSelector} from 'react-redux';
+import UpArrowIcon from '../../assets/icons/up_arrow.svg';
+import {TAGG_LIGHT_BLUE} from '../../constants';
+import {RootState} from '../../store/rootReducer';
+import {normalize} from '../../utils';
+import {Avatar} from '../common';
+
+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;
+ comment?: string;
+};
+
+const CommentTextField: FC<CommentTextFieldProps> = ({
+ containerStyle,
+ validateInput,
+ keyboardText,
+ partTypes,
+ renderMentionSuggestions,
+ handleTextInputRef,
+ onChangeInput,
+ handleSelectionChange,
+ parts,
+ addComment,
+ 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>
+ <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..d4a93bdd
--- /dev/null
+++ b/src/components/comments/CommentsCount.tsx
@@ -0,0 +1,53 @@
+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 {ScreenType} from '../../types';
+import {normalize} from '../../utils';
+
+interface CommentsCountProps {
+ momentId: string;
+ count: number;
+ screenType: ScreenType;
+}
+
+const CommentsCount: React.FC<CommentsCountProps> = ({
+ momentId,
+ count,
+ screenType,
+}) => {
+ const navigation = useNavigation();
+ return (
+ <TouchableOpacity
+ style={styles.countContainer}
+ onPress={() =>
+ navigation.navigate('MomentCommentsScreen', {
+ moment_id: momentId,
+ screenType,
+ })
+ }>
+ <CommentsIcon width={25} height={25} />
+ <Text style={styles.count}>{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';
diff --git a/src/components/common/MomentTags.tsx b/src/components/common/MomentTags.tsx
index bdd1fbeb..d8a70353 100644
--- a/src/components/common/MomentTags.tsx
+++ b/src/components/common/MomentTags.tsx
@@ -1,4 +1,5 @@
-import React, {createRef, MutableRefObject, useEffect, useState} from 'react';
+import React, {createRef, RefObject, useEffect, useState} from 'react';
+import {Image, View} from 'react-native';
import {MomentTagType, ProfilePreviewType} from '../../types';
import TaggDraggable from '../taggs/TaggDraggable';
import Draggable from './Draggable';
@@ -7,7 +8,7 @@ interface MomentTagsProps {
editing: boolean;
tags: MomentTagType[];
setTags: (tag: MomentTagType[]) => void;
- imageRef: MutableRefObject<null>;
+ imageRef: RefObject<Image>;
deleteFromList?: (user: ProfilePreviewType) => void;
}
@@ -21,14 +22,9 @@ const MomentTags: React.FC<MomentTagsProps> = ({
const [offset, setOffset] = useState([0, 0]);
const [imageDimensions, setImageDimensions] = useState([0, 0]);
const [maxZIndex, setMaxZIndex] = useState(1);
- const [draggableRefs, setDraggableRefs] = useState<
- React.MutableRefObject<null>[]
- >([]);
+ const [draggableRefs, setDraggableRefs] = useState<RefObject<View>[]>([]);
- const updateTagPosition = (
- ref: React.MutableRefObject<null>,
- userId: string,
- ) => {
+ const updateTagPosition = (ref: RefObject<Image>, userId: string) => {
if (ref !== null && ref.current !== null) {
ref.current.measure(
(
@@ -66,19 +62,21 @@ const MomentTags: React.FC<MomentTagsProps> = ({
useEffect(() => {
setTimeout(
() => {
- imageRef.current.measure(
- (
- fx: number, // location of ref relative to parent element
- fy: number,
- width: number,
- height: number,
- _x: number, // location of ref relative to entire screen
- _y: number,
- ) => {
- setOffset([fx, fy]);
- setImageDimensions([width, height]);
- },
- );
+ if (imageRef && imageRef.current) {
+ imageRef.current.measure(
+ (
+ fx: number, // location of ref relative to parent element
+ fy: number,
+ width: number,
+ height: number,
+ _x: number, // location of ref relative to entire screen
+ _y: number,
+ ) => {
+ setOffset([fx, fy]);
+ setImageDimensions([width, height]);
+ },
+ );
+ }
},
editing ? 100 : 0,
);
diff --git a/src/components/common/TaggTypeahead.tsx b/src/components/common/TaggTypeahead.tsx
index 747e0bb5..7967fdbc 100644
--- a/src/components/common/TaggTypeahead.tsx
+++ b/src/components/common/TaggTypeahead.tsx
@@ -1,21 +1,29 @@
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 = component === 'comment' ? -10 : 0;
useEffect(() => {
if (keyword === '') {
@@ -42,40 +50,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/IndividualMomentTitleBar.tsx b/src/components/moments/IndividualMomentTitleBar.tsx
index 4ae9471f..c6bf1423 100644
--- a/src/components/moments/IndividualMomentTitleBar.tsx
+++ b/src/components/moments/IndividualMomentTitleBar.tsx
@@ -1,45 +1,32 @@
import React from 'react';
-import {
- StyleSheet,
- Text,
- TouchableOpacity,
- View,
- ViewProps,
-} from 'react-native';
-import CloseIcon from '../../assets/ionicons/close-outline.svg';
-import {normalize} from '../../utils';
+import {StyleSheet, Text, View, ViewProps} from 'react-native';
+import {normalize, SCREEN_WIDTH} from '../../utils';
interface IndividualMomentTitleBarProps extends ViewProps {
title: string;
- close: () => void;
}
const IndividualMomentTitleBar: React.FC<IndividualMomentTitleBarProps> = ({
title,
- close,
- style,
}) => {
return (
- <View style={[styles.container, style]}>
- <TouchableOpacity style={styles.closeButton} onPress={close}>
- <CloseIcon height={'100%'} width={'100%'} color={'white'} />
- </TouchableOpacity>
- <View style={styles.headerContainer}>
- <Text style={styles.header}>{title}</Text>
+ <View style={styles.mainContainer}>
+ <View style={styles.titleContainer}>
+ <Text
+ style={[
+ styles.title,
+ {
+ fontSize: title.length > 18 ? normalize(14) : normalize(16),
+ },
+ ]}>
+ {title}
+ </Text>
</View>
</View>
);
};
const styles = StyleSheet.create({
- container: {
- flexDirection: 'row',
- alignItems: 'center',
- justifyContent: 'flex-start',
- },
- headerContainer: {
- width: '80%',
- },
- header: {
+ title: {
textAlign: 'center',
color: 'white',
fontSize: normalize(18),
@@ -47,10 +34,19 @@ const styles = StyleSheet.create({
lineHeight: normalize(21.48),
letterSpacing: normalize(1.3),
},
- closeButton: {
- height: '50%',
- aspectRatio: 1,
- left: '8%',
+ titleContainer: {
+ width: '80%',
+ position: 'absolute',
+ left: '10%',
+ right: '10%',
+ height: normalize(70),
+ },
+ mainContainer: {
+ flex: 1,
+ width: SCREEN_WIDTH * 0.6,
+ flexDirection: 'row',
+ justifyContent: 'flex-end',
+ marginVertical: '2%',
},
});
diff --git a/src/components/moments/Moment.tsx b/src/components/moments/Moment.tsx
index eab4b7e3..a43a2830 100644
--- a/src/components/moments/Moment.tsx
+++ b/src/components/moments/Moment.tsx
@@ -88,15 +88,11 @@ const Moment: React.FC<MomentProps> = ({
'Screenshots',
'UserLibrary',
],
- width: 580,
- height: 580,
- cropping: true,
- cropperToolbarTitle: 'Upload a moment',
- mediaType: 'photo',
+ mediaType: 'any',
})
.then((picture) => {
if (picture.path && picture.filename) {
- navigation.navigate('CaptionScreen', {
+ navigation.navigate('ZoomInCropper', {
screenType,
title,
media: {
@@ -161,12 +157,14 @@ const Moment: React.FC<MomentProps> = ({
onPress: () =>
ImagePicker.openCamera({
mediaType: 'video',
- }).then((vid) => {
- console.log(vid);
- if (vid.path) {
- navigateToCaptionScreenForVideo(vid.path);
- }
- }),
+ })
+ .then((vid) => {
+ console.log(vid);
+ if (vid.path) {
+ navigateToCaptionScreenForVideo(vid.path);
+ }
+ })
+ .catch((err) => console.error(err)),
},
])
}
diff --git a/src/components/moments/MomentCommentPreview.tsx b/src/components/moments/MomentCommentPreview.tsx
index e53ed258..232568f1 100644
--- a/src/components/moments/MomentCommentPreview.tsx
+++ b/src/components/moments/MomentCommentPreview.tsx
@@ -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={{
@@ -50,7 +52,7 @@ const MomentCommentPreview: React.FC<MomentCommentPreviewProps> = ({
{renderTextWithMentions({
value: commentPreview.comment,
styles: styles.normalFont,
- partTypes: mentionPartTypes('white'),
+ partTypes: mentionPartTypes('white', 'comment'),
onPress: (user: UserType) =>
navigateToProfile(
state,
diff --git a/src/components/moments/MomentPost.tsx b/src/components/moments/MomentPost.tsx
index d87028e3..cb3a138b 100644
--- a/src/components/moments/MomentPost.tsx
+++ b/src/components/moments/MomentPost.tsx
@@ -1,38 +1,85 @@
-import React, {useEffect, useState} from 'react';
-import {StyleSheet} from 'react-native';
-import {useSelector} from 'react-redux';
-import {MomentPostContent, MomentPostHeader} from '.';
+import {useNavigation} from '@react-navigation/native';
+import React, {useContext, useEffect, useRef, useState} from 'react';
+import {
+ Image,
+ KeyboardAvoidingView,
+ Platform,
+ StatusBar,
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+ TouchableWithoutFeedback,
+ View,
+} from 'react-native';
+import Animated, {EasingNode} from 'react-native-reanimated';
+import Video from 'react-native-video';
+import {useDispatch, useSelector, useStore} from 'react-redux';
+import {headerBarOptions} from '../../routes';
+import {MomentContext} from '../../screens/profile/IndividualMoment';
import {deleteMomentTag, loadMomentTags} from '../../services';
+import {loadUserMoments} from '../../store/actions';
import {RootState} from '../../store/rootReducer';
-import {MomentPostType, MomentTagType, ScreenType} from '../../types';
-import {normalize, SCREEN_HEIGHT} from '../../utils';
-
+import {MomentPostType, MomentTagType, ScreenType, UserType} from '../../types';
+import {
+ getTimePosted,
+ HeaderHeight,
+ isIPhoneX,
+ navigateToProfile,
+ normalize,
+ SCREEN_HEIGHT,
+ SCREEN_WIDTH,
+} from '../../utils';
+import {mentionPartTypes, renderTextWithMentions} from '../../utils/comments';
+import {AddComment} from '../comments';
+import CommentsCount from '../comments/CommentsCount';
+import {MomentTags} from '../common';
+import {MomentMoreInfoDrawer, TaggAvatar} from '../profile';
+import IndividualMomentTitleBar from './IndividualMomentTitleBar';
interface MomentPostProps {
moment: MomentPostType;
userXId: string | undefined;
screenType: ScreenType;
- index: number;
}
const MomentPost: React.FC<MomentPostProps> = ({
moment,
userXId,
screenType,
- index,
}) => {
+ const navigation = useNavigation();
+ const dispatch = useDispatch();
const {userId: loggedInUserId, username: loggedInUsername} = useSelector(
(state: RootState) => state.user.user,
);
-
- const {
- user: {username},
- } = useSelector((state: RootState) =>
+ const {user} = useSelector((state: RootState) =>
userXId ? state.userX[screenType][userXId] : state.user,
);
+ const state: RootState = useStore().getState();
+ const isOwnProfile = user.username === loggedInUsername;
+
const [tags, setTags] = useState<MomentTagType[]>([]);
+ const [visible, setVisible] = useState(false);
+ const [drawerVisible, setDrawerVisible] = useState(false);
+ const [hideText, setHideText] = useState(false);
+
+ const [fadeValue, setFadeValue] = useState<Animated.Value<number>>(
+ new Animated.Value(0),
+ );
+ const [commentCount, setCommentCount] = useState<number>(
+ moment.comments_count,
+ );
+ const [aspectRatio, setAspectRatio] = useState<number>(1);
const [momentTagId, setMomentTagId] = useState<string>('');
- const isOwnProfile = username === loggedInUsername;
+ const imageRef = useRef(null);
+ const videoRef = useRef<Video>(null);
+ const {keyboardVisible, currentVisibleMomentId} = useContext(MomentContext);
+ const isVideo = !(
+ moment.moment_url.endsWith('jpg') ||
+ moment.moment_url.endsWith('JPG') ||
+ moment.moment_url.endsWith('PNG') ||
+ moment.moment_url.endsWith('png')
+ );
/*
* Load tags on initial render to pass tags data to moment header and content
@@ -41,7 +88,7 @@ const MomentPost: React.FC<MomentPostProps> = ({
loadMomentTags(moment.moment_id).then((response) => {
setTags(response ? response : []);
});
- }, []);
+ }, [moment]);
/*
* Check if loggedInUser has been tagged in the picture and set the id
@@ -71,34 +118,311 @@ const MomentPost: React.FC<MomentPostProps> = ({
}
};
+ useEffect(
+ () =>
+ navigation.setOptions({
+ ...headerBarOptions('white', ''),
+ headerTitle: () => (
+ <IndividualMomentTitleBar title={moment.moment_category} />
+ ),
+ }),
+ [moment.moment_id],
+ );
+
+ /*
+ * Determines if an image is 9:16 to set aspect ratio of current image and
+ * determine if image must be displayed in full screen or not
+ */
+ useEffect(() => {
+ if (!isVideo) {
+ Image.getSize(
+ moment.moment_url,
+ (w, h) => {
+ setAspectRatio(w / h);
+ },
+ (err) => console.log(err),
+ );
+ }
+ }, []);
+
+ /*
+ * To animate tags display
+ */
+ useEffect(() => {
+ const fade = async () => {
+ Animated.timing(fadeValue, {
+ toValue: 1,
+ duration: 250,
+ easing: EasingNode.linear,
+ }).start();
+ };
+ fade();
+ }, [fadeValue]);
+
+ useEffect(() => {
+ if (!keyboardVisible && hideText) {
+ setHideText(false);
+ }
+ }, [keyboardVisible, hideText]);
+
+ useEffect(() => {
+ if (moment.moment_id !== currentVisibleMomentId) {
+ videoRef.current?.seek(0);
+ }
+ }, [currentVisibleMomentId]);
+
+ const MomentPosterPreview = () => (
+ <View style={styles.momentPosterContainer}>
+ <TouchableOpacity
+ onPress={() =>
+ navigateToProfile(state, dispatch, navigation, screenType, user)
+ }
+ style={styles.header}>
+ <TaggAvatar
+ style={styles.avatar}
+ userXId={userXId}
+ screenType={screenType}
+ editable={false}
+ />
+ <Text style={styles.headerText}>{user.username}</Text>
+ </TouchableOpacity>
+ </View>
+ );
+
return (
<>
- <MomentPostHeader
- style={styles.postHeader}
- userXId={userXId}
- screenType={screenType}
- username={isOwnProfile ? loggedInUsername : username}
- momentTagId={momentTagId}
- removeTag={removeTag}
- moment={moment}
- tags={tags}
- />
- <MomentPostContent
- style={styles.postContent}
- moment={moment}
- screenType={screenType}
- momentTags={tags}
- index={index}
- />
+ <StatusBar barStyle={'light-content'} />
+ <View style={styles.mainContainer}>
+ <View style={styles.imageContainer}>
+ {isVideo ? (
+ <View
+ ref={imageRef}
+ style={[
+ styles.media,
+ {
+ height: SCREEN_WIDTH / aspectRatio,
+ },
+ ]}>
+ <Video
+ ref={videoRef}
+ source={{
+ uri: moment.moment_url,
+ }}
+ volume={1}
+ style={[
+ styles.media,
+ {
+ height: SCREEN_WIDTH / aspectRatio,
+ },
+ ]}
+ repeat={true}
+ resizeMode={'contain'}
+ onLoad={(response) => {
+ const {width, height} = response.naturalSize;
+ setAspectRatio(width / height);
+ }}
+ paused={moment.moment_id !== currentVisibleMomentId}
+ />
+ </View>
+ ) : (
+ <Image
+ source={{uri: moment.moment_url}}
+ style={styles.media}
+ resizeMode={'contain'}
+ ref={imageRef}
+ />
+ )}
+ </View>
+ {visible && (
+ <Animated.View style={[styles.tagsContainer, {opacity: fadeValue}]}>
+ <MomentTags
+ editing={false}
+ tags={tags}
+ setTags={() => null}
+ imageRef={imageRef}
+ />
+ </Animated.View>
+ )}
+ <TouchableWithoutFeedback
+ onPress={() => {
+ setVisible(!visible);
+ setFadeValue(new Animated.Value(0));
+ }}>
+ <View style={styles.contentContainer}>
+ <View style={styles.topContainer}>
+ <MomentMoreInfoDrawer
+ isOpen={drawerVisible}
+ setIsOpen={setDrawerVisible}
+ isOwnProfile={isOwnProfile}
+ momentTagId={momentTagId}
+ removeTag={removeTag}
+ dismissScreenAndUpdate={() => {
+ dispatch(loadUserMoments(loggedInUserId));
+ navigation.goBack();
+ }}
+ screenType={screenType}
+ moment={moment}
+ tags={tags}
+ />
+ </View>
+ <KeyboardAvoidingView
+ behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
+ keyboardVerticalOffset={-20}>
+ <View style={styles.bottomContainer}>
+ {tags.length > 0 && (
+ <Image
+ source={require('../../assets/icons/tag_indicate.png')}
+ style={styles.tagIcon}
+ />
+ )}
+ <View style={styles.commentsCountContainer}>
+ <CommentsCount
+ momentId={moment.moment_id}
+ count={commentCount}
+ screenType={screenType}
+ />
+ </View>
+ <MomentPosterPreview />
+ {!hideText && (
+ <>
+ {moment.caption !== '' &&
+ renderTextWithMentions({
+ value: moment.caption,
+ styles: styles.captionText,
+ partTypes: mentionPartTypes('white', 'caption'),
+ onPress: (userLocal: UserType) =>
+ navigateToProfile(
+ state,
+ dispatch,
+ navigation,
+ screenType,
+ userLocal,
+ ),
+ })}
+ </>
+ )}
+ <View>
+ <AddComment
+ placeholderText={'Add a comment here!'}
+ momentId={moment.moment_id}
+ callback={() => {
+ setCommentCount(commentCount + 1);
+ }}
+ onFocus={() => {
+ setHideText(true);
+ }}
+ isKeyboardAvoiding={false}
+ theme={'dark'}
+ />
+ <Text style={styles.text}>
+ {getTimePosted(moment.date_created)}
+ </Text>
+ </View>
+ </View>
+ </KeyboardAvoidingView>
+ </View>
+ </TouchableWithoutFeedback>
+ </View>
</>
);
};
const styles = StyleSheet.create({
- postHeader: {},
- postContent: {
- minHeight: SCREEN_HEIGHT * 0.8,
- paddingBottom: normalize(20),
+ media: {
+ zIndex: 0,
+ flex: 1,
+ },
+ imageContainer: {
+ height: SCREEN_HEIGHT,
+ width: SCREEN_WIDTH,
+ flexDirection: 'column',
+ justifyContent: 'center',
+ overflow: 'hidden',
+ },
+ text: {
+ marginHorizontal: '5%',
+ color: 'white',
+ fontWeight: '500',
+ textAlign: 'right',
+ marginTop: 5,
+ },
+ captionText: {
+ position: 'relative',
+ marginHorizontal: '5%',
+ color: '#ffffff',
+ fontWeight: '500',
+ fontSize: normalize(13),
+ lineHeight: normalize(15.51),
+ letterSpacing: normalize(0.6),
+ marginBottom: normalize(5),
+ width: SCREEN_WIDTH * 0.79,
+ },
+ tagIcon: {
+ width: normalize(30),
+ height: normalize(30),
+ bottom: normalize(20),
+ left: '5%',
+ },
+ avatar: {
+ width: 48,
+ aspectRatio: 1,
+ borderRadius: 100,
+ marginLeft: '3%',
+ },
+ headerText: {
+ fontSize: 15,
+ fontWeight: 'bold',
+ color: 'white',
+ paddingHorizontal: '3%',
+ },
+ header: {
+ alignItems: 'center',
+ flexDirection: 'row',
+ marginBottom: normalize(15),
+ alignSelf: 'flex-start',
+ },
+ momentPosterContainer: {
+ flexDirection: 'column',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ commentsCountContainer: {
+ position: 'absolute',
+ zIndex: 3,
+ right: '2%',
+ bottom: SCREEN_HEIGHT * 0.12,
+ },
+ bottomContainer: {
+ flexDirection: 'column',
+ justifyContent: 'flex-end',
+ },
+ topContainer: {
+ paddingTop: isIPhoneX() ? HeaderHeight : '6%',
+ alignSelf: 'flex-end',
+ paddingRight: '8%',
+ },
+ contentContainer: {
+ position: 'absolute',
+ width: SCREEN_WIDTH,
+ height: SCREEN_HEIGHT,
+ flexDirection: 'column',
+ justifyContent: 'space-between',
+ paddingBottom: '24%',
+ },
+ tagsContainer: {
+ position: 'absolute',
+ top: 0,
+ bottom: 0,
+ left: 0,
+ right: 0,
+ marginBottom: '3%',
+ },
+ mainContainer: {
+ backgroundColor: 'black',
+ width: SCREEN_WIDTH,
+ height: SCREEN_HEIGHT,
+ flexDirection: 'column',
+ justifyContent: 'center',
},
});
diff --git a/src/components/moments/MomentPostHeader.tsx b/src/components/moments/MomentPostHeader.tsx
deleted file mode 100644
index 5f26951a..00000000
--- a/src/components/moments/MomentPostHeader.tsx
+++ /dev/null
@@ -1,116 +0,0 @@
-import {useNavigation} from '@react-navigation/native';
-import React, {useState} from 'react';
-import {
- StyleSheet,
- Text,
- TouchableOpacity,
- View,
- ViewProps,
-} from 'react-native';
-import {useDispatch, useSelector, useStore} from 'react-redux';
-import {loadUserMoments} from '../../store/actions';
-import {RootState} from '../../store/rootReducer';
-import {MomentTagType, MomentType, ScreenType} from '../../types';
-import {fetchUserX, userXInStore} from '../../utils';
-import {MomentMoreInfoDrawer} from '../profile';
-import TaggAvatar from '../profile/TaggAvatar';
-
-interface MomentPostHeaderProps extends ViewProps {
- userXId?: string;
- screenType: ScreenType;
- username: string;
- momentTagId: string;
- removeTag: () => Promise<void>;
- moment: MomentType;
- tags: MomentTagType[];
-}
-
-const MomentPostHeader: React.FC<MomentPostHeaderProps> = ({
- userXId,
- screenType,
- username,
- style,
- momentTagId,
- removeTag,
- moment,
- tags,
-}) => {
- const [drawerVisible, setDrawerVisible] = useState(false);
- const dispatch = useDispatch();
- const navigation = useNavigation();
- const {userId: loggedInUserId, username: loggedInUserName} = useSelector(
- (state: RootState) => state.user.user,
- );
- const state: RootState = useStore().getState();
- const isOwnProfile = loggedInUserName === username;
- const navigateToProfile = async () => {
- if (userXId && !userXInStore(state, screenType, userXId)) {
- await fetchUserX(
- dispatch,
- {userId: userXId, username: username},
- screenType,
- );
- }
- navigation.navigate('Profile', {
- userXId: isOwnProfile ? undefined : userXId,
- screenType,
- });
- };
-
- return (
- <View style={[styles.container, style]}>
- <TouchableOpacity onPress={navigateToProfile} style={styles.header}>
- <TaggAvatar
- style={styles.avatar}
- userXId={userXId}
- screenType={screenType}
- editable={false}
- />
- <Text style={styles.headerText}>{username}</Text>
- </TouchableOpacity>
- <MomentMoreInfoDrawer
- isOpen={drawerVisible}
- setIsOpen={setDrawerVisible}
- isOwnProfile={isOwnProfile}
- momentTagId={momentTagId}
- removeTag={removeTag}
- dismissScreenAndUpdate={() => {
- dispatch(loadUserMoments(loggedInUserId));
- navigation.goBack();
- }}
- screenType={screenType}
- moment={moment}
- tags={tags}
- />
- </View>
- );
-};
-
-const styles = StyleSheet.create({
- container: {
- flex: 1,
- justifyContent: 'space-around',
- flexDirection: 'row',
- alignItems: 'center',
- marginVertical: '2%',
- },
- header: {
- alignItems: 'center',
- flexDirection: 'row',
- flex: 1,
- },
- avatar: {
- flex: 0.2,
- aspectRatio: 1,
- borderRadius: 999999,
- marginLeft: '3%',
- },
- headerText: {
- fontSize: 15,
- fontWeight: 'bold',
- color: 'white',
- paddingHorizontal: '3%',
- flex: 1,
- },
-});
-export default MomentPostHeader;
diff --git a/src/components/moments/index.ts b/src/components/moments/index.ts
index c1419cfd..cac2da2e 100644
--- a/src/components/moments/index.ts
+++ b/src/components/moments/index.ts
@@ -1,7 +1,5 @@
export {default as IndividualMomentTitleBar} from './IndividualMomentTitleBar';
export {default as CaptionScreenHeader} from './CaptionScreenHeader';
-export {default as MomentPostHeader} from './MomentPostHeader';
-export {default as MomentPostContent} from './MomentPostContent';
export {default as Moment} from './Moment';
export {default as TagFriendsFooter} from './TagFriendsFoooter';
export {default as MomentPost} from './MomentPost';
diff --git a/src/components/moments/MomentPostContent.tsx b/src/components/moments/legacy/MomentPostContent.tsx
index 4d2554d8..0e6e5eed 100644
--- a/src/components/moments/MomentPostContent.tsx
+++ b/src/components/moments/legacy/MomentPostContent.tsx
@@ -5,39 +5,40 @@ import {TouchableWithoutFeedback} from 'react-native-gesture-handler';
import Animated, {EasingNode} from 'react-native-reanimated';
import Video from 'react-native-video';
import {useDispatch, useStore} from 'react-redux';
-import {MomentContext} from '../../screens/profile/IndividualMoment';
-import {RootState} from '../../store/rootReducer';
+import {MomentContext} from '../../../screens/profile/IndividualMoment';
+import {RootState} from '../../../store/rootReducer';
import {
MomentCommentPreviewType,
MomentPostType,
MomentTagType,
ScreenType,
UserType,
-} from '../../types';
+} from '../../../types';
import {
getLoggedInUserAsProfilePreview,
getTimePosted,
navigateToProfile,
normalize,
SCREEN_WIDTH,
-} from '../../utils';
-import {mentionPartTypes, renderTextWithMentions} from '../../utils/comments';
-import {AddComment} from '../comments';
-import {MomentTags} from '../common';
-import MomentCommentPreview from './MomentCommentPreview';
+} from '../../../utils';
+import {
+ mentionPartTypes,
+ renderTextWithMentions,
+} from '../../../utils/comments';
+import {AddComment} from '../../comments';
+import {MomentTags} from '../../common';
+import MomentCommentPreview from '../MomentCommentPreview';
interface MomentPostContentProps extends ViewProps {
screenType: ScreenType;
moment: MomentPostType;
momentTags: MomentTagType[];
- index: number;
}
const MomentPostContent: React.FC<MomentPostContentProps> = ({
screenType,
moment,
style,
momentTags,
- index,
}) => {
const [tags, setTags] = useState<MomentTagType[]>(momentTags);
const state: RootState = useStore().getState();
@@ -53,7 +54,7 @@ const MomentPostContent: React.FC<MomentPostContentProps> = ({
);
const [commentPreview, setCommentPreview] =
useState<MomentCommentPreviewType | null>(moment.comment_preview);
- const {keyboardVisible, scrollTo} = useContext(MomentContext);
+ const {keyboardVisible} = useContext(MomentContext);
const [hideText, setHideText] = useState(false);
const isVideo = !(
moment.moment_url.endsWith('jpg') ||
@@ -140,7 +141,7 @@ const MomentPostContent: React.FC<MomentPostContentProps> = ({
renderTextWithMentions({
value: moment.caption,
styles: styles.captionText,
- partTypes: mentionPartTypes('white'),
+ partTypes: mentionPartTypes('white', 'caption'),
onPress: (user: UserType) =>
navigateToProfile(
state,
@@ -170,7 +171,6 @@ const MomentPostContent: React.FC<MomentPostContentProps> = ({
}}
onFocus={() => {
setHideText(true);
- scrollTo(index);
}}
isKeyboardAvoiding={false}
theme={'dark'}
diff --git a/src/components/profile/MomentMoreInfoDrawer.tsx b/src/components/profile/MomentMoreInfoDrawer.tsx
index c70df875..910aa095 100644
--- a/src/components/profile/MomentMoreInfoDrawer.tsx
+++ b/src/components/profile/MomentMoreInfoDrawer.tsx
@@ -3,7 +3,6 @@ import React, {useEffect, useState} from 'react';
import {
Alert,
GestureResponderEvent,
- StyleSheet,
TextStyle,
TouchableOpacity,
ViewProps,
@@ -174,7 +173,6 @@ const MomentMoreInfoDrawer: React.FC<MomentMoreInfoDrawerProps> = (props) => {
return (
<>
<TouchableOpacity
- style={styles.icon}
onPress={() => {
setIsOpen(true);
}}>
@@ -189,10 +187,4 @@ const MomentMoreInfoDrawer: React.FC<MomentMoreInfoDrawerProps> = (props) => {
);
};
-const styles = StyleSheet.create({
- icon: {
- marginRight: '3%',
- },
-});
-
export default MomentMoreInfoDrawer;
diff --git a/src/components/profile/PublicProfile.tsx b/src/components/profile/PublicProfile.tsx
index 8a80c56f..6b991d7c 100644
--- a/src/components/profile/PublicProfile.tsx
+++ b/src/components/profile/PublicProfile.tsx
@@ -87,7 +87,9 @@ const PublicProfile: React.FC<ContentProps> = ({
scrollViewRef.current
) {
setScrollEnabled(false);
- scrollViewRef.current.scrollTo({y: 0});
+ if (scrollViewRef && scrollViewRef.current) {
+ scrollViewRef.current.scrollTo({y: 0});
+ }
navigation.navigate('MomentUploadPrompt', {
screenType,
momentCategory: momentCategories[0],
diff --git a/src/components/taggs/TaggDraggable.tsx b/src/components/taggs/TaggDraggable.tsx
index d458fab6..ea19591d 100644
--- a/src/components/taggs/TaggDraggable.tsx
+++ b/src/components/taggs/TaggDraggable.tsx
@@ -1,5 +1,5 @@
import {useNavigation} from '@react-navigation/native';
-import React from 'react';
+import React, {RefObject} from 'react';
import {
Image,
StyleSheet,
@@ -17,7 +17,7 @@ import {normalize} from '../../utils';
import {navigateToProfile} from '../../utils/users';
interface TaggDraggableProps extends ViewProps {
- draggableRef: React.MutableRefObject<null>;
+ draggableRef: RefObject<View>;
taggedUser: ProfilePreviewType;
editingView: boolean;
deleteFromList: () => void;
diff --git a/src/constants/strings.ts b/src/constants/strings.ts
index a1064f49..112bc546 100644
--- a/src/constants/strings.ts
+++ b/src/constants/strings.ts
@@ -77,7 +77,7 @@ You've been tagged by ${invitee}. Follow the instructions below to skip the line
Sign up and use this code to get in: ${inviteCode}\n ${APP_STORE_LINK}`;
export const SUCCESS_LAST_CONTACT_INVITE = 'Done! That was your last invite, hope you used it wisely!';
export const SUCCESS_LINK = (str: string) => `Successfully linked ${str} 🎉`;
-export const SUCCESS_PIC_UPLOAD = 'Beautiful, the picture was uploaded successfully!';
+export const SUCCESS_PIC_UPLOAD = 'Beautiful, the Moment was uploaded successfully!';
export const SUCCESS_PWD_RESET = 'Your password was reset successfully!';
export const SUCCESS_VERIFICATION_CODE_SENT = 'New verification code sent! Check your phone messages for your code';
export const UP_TO_DATE = 'Up-to-Date!';
diff --git a/src/routes/main/MainStackNavigator.tsx b/src/routes/main/MainStackNavigator.tsx
index 522a18dd..c518d75e 100644
--- a/src/routes/main/MainStackNavigator.tsx
+++ b/src/routes/main/MainStackNavigator.tsx
@@ -110,6 +110,11 @@ export type MainStackParams = {
ChatList: undefined;
Chat: undefined;
NewChatModal: undefined;
+ ZoomInCropper: {
+ media: {filename: string; uri: string; isVideo: boolean};
+ screenType: ScreenType;
+ title: string;
+ };
};
export const MainStack = createStackNavigator<MainStackParams>();
diff --git a/src/routes/main/MainStackScreen.tsx b/src/routes/main/MainStackScreen.tsx
index 3be2ff28..9e3747f9 100644
--- a/src/routes/main/MainStackScreen.tsx
+++ b/src/routes/main/MainStackScreen.tsx
@@ -39,6 +39,7 @@ import MutualBadgeHolders from '../../screens/suggestedPeople/MutualBadgeHolders
import {ScreenType} from '../../types';
import {AvatarHeaderHeight, ChatHeaderHeight, SCREEN_WIDTH} from '../../utils';
import {MainStack, MainStackParams} from './MainStackNavigator';
+import {ZoomInCropper} from '../../components/comments/ZoomInCropper';
/**
* Profile : To display the logged in user's profile when the userXId passed in to it is (undefined | null | empty string) else displays profile of the user being visited.
@@ -209,6 +210,7 @@ const MainStackScreen: React.FC<MainStackProps> = ({route}) => {
options={{
...modalStyle,
gestureEnabled: false,
+ ...headerBarOptions('white', ''),
}}
/>
<MainStack.Screen
@@ -325,6 +327,13 @@ const MainStackScreen: React.FC<MainStackProps> = ({route}) => {
gestureEnabled: false,
}}
/>
+ <MainStack.Screen
+ name="ZoomInCropper"
+ component={ZoomInCropper}
+ options={{
+ gestureEnabled: false,
+ }}
+ />
</MainStack.Navigator>
);
};
diff --git a/src/screens/moments/TagFriendsScreen.tsx b/src/screens/moments/TagFriendsScreen.tsx
index bda38651..201caf49 100644
--- a/src/screens/moments/TagFriendsScreen.tsx
+++ b/src/screens/moments/TagFriendsScreen.tsx
@@ -27,6 +27,8 @@ const TagFriendsScreen: React.FC<TagFriendsScreenProps> = ({route}) => {
const navigation = useNavigation();
const imageRef = useRef(null);
const [tags, setTags] = useState<MomentTagType[]>([]);
+ const [imageWidth, setImageWidth] = useState<number>(0);
+ const [imageHeight, setImageHeight] = useState<number>(0);
/*
* Update list of tagged users from route params
@@ -45,6 +47,36 @@ const TagFriendsScreen: React.FC<TagFriendsScreenProps> = ({route}) => {
});
};
+ const setMediaDimensions = (width: number, height: number) => {
+ const imageAspectRatio = width / height;
+
+ // aspectRatio: >= 1 [Landscape] [1:1]
+ if (imageAspectRatio >= 1) {
+ setImageWidth(SCREEN_WIDTH);
+ setImageHeight(SCREEN_WIDTH / imageAspectRatio);
+ }
+ // aspectRatio: < 1 [Portrait]
+ if (imageAspectRatio < 1) {
+ setImageHeight(SCREEN_WIDTH);
+ setImageWidth(SCREEN_WIDTH * imageAspectRatio);
+ }
+ };
+
+ /*
+ * Calculating image width and height with respect to it's enclosing view's dimensions. Only works for images.
+ */
+ useEffect(() => {
+ if (imageRef && imageRef.current && !media.isVideo) {
+ Image.getSize(
+ media.uri,
+ (w, h) => {
+ setMediaDimensions(w, h);
+ },
+ (err) => console.log(err),
+ );
+ }
+ }, []);
+
return (
<SearchBackground>
<View style={styles.contentContainer}>
@@ -72,19 +104,42 @@ const TagFriendsScreen: React.FC<TagFriendsScreenProps> = ({route}) => {
})
}>
{media.isVideo ? (
- <View style={styles.media} ref={imageRef}>
+ <View
+ style={{
+ width: imageWidth,
+ height: imageHeight,
+ marginVertical: (SCREEN_WIDTH - imageHeight) / 2,
+ marginHorizontal: (SCREEN_WIDTH - imageWidth) / 2,
+ }}
+ ref={imageRef}>
<Video
- style={styles.media}
+ style={{
+ width: imageWidth,
+ height: imageHeight,
+ }}
source={{uri: media.uri}}
repeat={true}
+ onLoad={(response) => {
+ const {width, height, orientation} = response.naturalSize;
+ // portrait will flip width and height
+ if (orientation === 'portrait') {
+ setMediaDimensions(height, width);
+ } else {
+ setMediaDimensions(width, height);
+ }
+ }}
/>
</View>
) : (
<Image
ref={imageRef}
- style={styles.media}
+ style={{
+ width: imageWidth,
+ height: imageHeight,
+ marginVertical: (SCREEN_WIDTH - imageHeight) / 2,
+ marginHorizontal: (SCREEN_WIDTH - imageWidth) / 2,
+ }}
source={{uri: media.uri}}
- resizeMode={'cover'}
/>
)}
</TouchableWithoutFeedback>
@@ -127,24 +182,10 @@ const styles = StyleSheet.create({
header: {
marginVertical: 20,
},
- media: {
- position: 'relative',
- width: SCREEN_WIDTH,
- aspectRatio: 1,
- marginBottom: '3%',
- },
- text: {
- position: 'relative',
- backgroundColor: 'white',
- width: '100%',
- paddingHorizontal: '2%',
- paddingVertical: '1%',
- height: 60,
- },
- flex: {
- flex: 1,
+ footerContainer: {
+ marginHorizontal: '5%',
+ marginTop: '3%',
},
- footerContainer: {marginHorizontal: '5%', marginTop: '3%'},
});
export default TagFriendsScreen;
diff --git a/src/screens/profile/CaptionScreen.tsx b/src/screens/profile/CaptionScreen.tsx
index d53570cb..364b81a3 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 Video from 'react-native-video';
import {useDispatch, useSelector} from 'react-redux';
@@ -237,16 +237,16 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => {
<Image
style={styles.media}
source={{uri: mediaUri}}
- resizeMode={'cover'}
+ resizeMode={'contain'}
/>
)}
- <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/screens/profile/IndividualMoment.tsx b/src/screens/profile/IndividualMoment.tsx
index f8113aba..a322b1e9 100644
--- a/src/screens/profile/IndividualMoment.tsx
+++ b/src/screens/profile/IndividualMoment.tsx
@@ -1,28 +1,16 @@
-import {BlurView} from '@react-native-community/blur';
import {RouteProp} from '@react-navigation/native';
import {StackNavigationProp} from '@react-navigation/stack';
import React, {useEffect, useRef, useState} from 'react';
-import {FlatList, Keyboard, StyleSheet} from 'react-native';
+import {FlatList, Keyboard, ViewToken} from 'react-native';
import {useSelector} from 'react-redux';
-import {IndividualMomentTitleBar, MomentPost} from '../../components';
-import {AVATAR_DIM} from '../../constants';
+import {MomentPost, TabsGradient} from '../../components';
import {MainStackParams} from '../../routes';
import {RootState} from '../../store/rootreducer';
import {MomentPostType} from '../../types';
-import {
- isIPhoneX,
- normalize,
- SCREEN_HEIGHT,
- StatusBarHeight,
-} from '../../utils';
-
-/**
- * Individual moment view opened when user clicks on a moment tile
- */
type MomentContextType = {
keyboardVisible: boolean;
- scrollTo: (index: number) => void;
+ currentVisibleMomentId: string | undefined;
};
export const MomentContext = React.createContext({} as MomentContextType);
@@ -39,10 +27,7 @@ interface IndividualMomentProps {
navigation: IndividualMomentNavigationProp;
}
-const IndividualMoment: React.FC<IndividualMomentProps> = ({
- route,
- navigation,
-}) => {
+const IndividualMoment: React.FC<IndividualMomentProps> = ({route}) => {
const {
userXId,
screenType,
@@ -57,6 +42,18 @@ const IndividualMoment: React.FC<IndividualMomentProps> = ({
);
const initialIndex = momentData.findIndex((m) => m.moment_id === moment_id);
const [keyboardVisible, setKeyboardVisible] = useState(false);
+ const [currentVisibleMomentId, setCurrentVisibleMomentId] = useState<
+ string | undefined
+ >();
+ // https://stackoverflow.com/a/57502343
+ const viewabilityConfigCallback = useRef(
+ (info: {viewableItems: ViewToken[]; changed: ViewToken[]}) => {
+ const index = info.viewableItems[0].index;
+ if (index !== null) {
+ setCurrentVisibleMomentId(momentData[index].moment_id);
+ }
+ },
+ );
useEffect(() => {
const showKeyboard = () => setKeyboardVisible(true);
@@ -69,70 +66,39 @@ const IndividualMoment: React.FC<IndividualMomentProps> = ({
};
}, []);
- const scrollTo = (index: number) => {
- // TODO: make this dynamic
- const offset = isIPhoneX() ? -(AVATAR_DIM + 100) : -(AVATAR_DIM + 160);
- scrollRef.current?.scrollToIndex({
- index: index,
- viewOffset: offset,
- });
- };
-
return (
<MomentContext.Provider
value={{
keyboardVisible,
- scrollTo,
+ currentVisibleMomentId,
}}>
- <BlurView
- blurType="light"
- blurAmount={30}
- reducedTransparencyFallbackColor="white"
- style={styles.contentContainer}>
- <IndividualMomentTitleBar
- style={styles.header}
- close={() => navigation.goBack()}
- title={moment_category}
- />
- <FlatList
- ref={scrollRef}
- data={momentData}
- contentContainerStyle={styles.listContentContainer}
- renderItem={({item, index}) => (
- <MomentPost
- moment={item}
- userXId={userXId}
- screenType={screenType}
- index={index}
- />
- )}
- keyExtractor={(item, _) => item.moment_id}
- showsVerticalScrollIndicator={false}
- initialScrollIndex={initialIndex}
- onScrollToIndexFailed={() => {
- // TODO: code below does not work, index resets to 0
- // const wait = new Promise((resolve) => setTimeout(resolve, 500));
- // wait.then(() => {
- // console.log('scrolling to ', initialIndex);
- // scrollRef.current?.scrollToIndex({index: initialIndex});
- // });
- }}
- />
- </BlurView>
+ <FlatList
+ ref={scrollRef}
+ data={momentData}
+ renderItem={({item}) => (
+ <MomentPost
+ key={item.moment_id}
+ moment={item}
+ userXId={userXId}
+ screenType={screenType}
+ />
+ )}
+ keyExtractor={(item, _) => item.moment_id}
+ showsVerticalScrollIndicator={false}
+ initialScrollIndex={initialIndex}
+ onViewableItemsChanged={viewabilityConfigCallback.current}
+ onScrollToIndexFailed={(info) => {
+ setTimeout(() => {
+ scrollRef.current?.scrollToIndex({
+ index: info.index,
+ });
+ }, 500);
+ }}
+ pagingEnabled
+ />
+ <TabsGradient />
</MomentContext.Provider>
);
};
-const styles = StyleSheet.create({
- contentContainer: {
- paddingTop: StatusBarHeight,
- flex: 1,
- },
- header: {
- height: normalize(70),
- },
- listContentContainer: {
- paddingBottom: SCREEN_HEIGHT * 0.2,
- },
-});
export default IndividualMoment;
diff --git a/src/screens/profile/MomentCommentsScreen.tsx b/src/screens/profile/MomentCommentsScreen.tsx
index 7dfe8ae9..402e5f44 100644
--- a/src/screens/profile/MomentCommentsScreen.tsx
+++ b/src/screens/profile/MomentCommentsScreen.tsx
@@ -48,9 +48,8 @@ const MomentCommentsScreen: React.FC<MomentCommentsScreenProps> = ({route}) => {
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 [commentTapped, setCommentTapped] = useState<
- CommentType | CommentThreadType | undefined
- >();
+ const [commentTapped, setCommentTapped] =
+ useState<CommentType | CommentThreadType | undefined>();
useEffect(() => {
navigation.setOptions({
diff --git a/src/services/MomentService.ts b/src/services/MomentService.ts
index ca32a3f3..b274ef04 100644
--- a/src/services/MomentService.ts
+++ b/src/services/MomentService.ts
@@ -241,7 +241,6 @@ export const handlePresignedURL = async (
const status = response.status;
let data: PresignedURLResponse = await response.json();
if (status === 200) {
- console.log('done');
return data;
} else {
if (status === 404) {
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),
diff --git a/yarn.lock b/yarn.lock
index f1a60330..9b425078 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1373,9 +1373,9 @@
"@types/react-native" "*"
"@types/react-native-video@^5.0.6":
- version "5.0.6"
- resolved "https://registry.yarnpkg.com/@types/react-native-video/-/react-native-video-5.0.6.tgz#1a84eff00820b8d3136dd6d6333ce471738293a6"
- integrity sha512-elf7Y7qornIySK5yR9yOINtQcqXScSQoe9yz5Kuisq/IV5Kf3BprsNGHloRXT+WYVQflii6+EJwyAUs1R4izAw==
+ version "5.0.7"
+ resolved "https://registry.yarnpkg.com/@types/react-native-video/-/react-native-video-5.0.7.tgz#f1d05698c62f2c9b54cff6cf7050bca2052bac99"
+ integrity sha512-ITJ9K3jEaP1hX58JhLvXJHGdNAkIKAYDxGZMiYys+5h3AOjFmdg7B3lqmoOc5+dGL41op3h1dNDoqstDD+H8eg==
dependencies:
"@types/react" "*"
"@types/react-native" "*"
@@ -2348,6 +2348,11 @@ color-convert@^2.0.1:
dependencies:
color-name "~1.1.4"
+color-convert@~0.5.0:
+ version "0.5.3"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.5.3.tgz#bdb6c69ce660fadffe0b0007cc447e1b9f7282bd"
+ integrity sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=
+
color-name@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
@@ -2825,9 +2830,9 @@ ee-first@1.1.1:
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
electron-to-chromium@^1.3.723:
- version "1.3.754"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.754.tgz#afbe69177ad7aae968c3bbeba129dc70dcc37cf4"
- integrity sha512-Q50dJbfYYRtwK3G9mFP/EsJVzlgcYwKxFjbXmvVa1lDAbdviPcT9QOpFoufDApub4j0hBfDRL6v3lWNLEdEDXQ==
+ version "1.3.756"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.756.tgz#942cee59cd64d19f576d8d5804eef09cb423740c"
+ integrity sha512-WsmJym1TMeHVndjPjczTFbnRR/c4sbzg8fBFtuhlb2Sru3i/S1VGpzDSrv/It8ctMU2bj8G7g7/O3FzYMGw6eA==
eme-encryption-scheme-polyfill@^2.0.1:
version "2.0.3"
@@ -5790,9 +5795,9 @@ node-releases@^1.1.71:
integrity sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==
node-stream-zip@^1.9.1:
- version "1.13.5"
- resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.13.5.tgz#90c28b1b8fe8f7c40a72bd30a7c1a8cfae61b715"
- integrity sha512-Lfi9xhSNvnJU35+4ZFlECXKJ70etAgJYWAVCdcEpksPnMrgwNqwkCJqdunoViVoFFV38C7AIodYE+2apuoK9Gw==
+ version "1.13.6"
+ resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.13.6.tgz#8abdfdbc4bc96ee11e9438d94cc8c93c7df28959"
+ integrity sha512-c7tRSVkLNOHvasWgmZ2d86cDgTWEygnkuuHNOY9c0mR3yLZtQTTrGvMaJ/fPs6+LOJn3240y30l5sjLaXFtcvw==
normalize-package-data@^2.3.2:
version "2.5.0"
@@ -6074,6 +6079,13 @@ parent-module@^1.0.0:
dependencies:
callsites "^3.0.0"
+parse-color@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/parse-color/-/parse-color-1.0.0.tgz#7b748b95a83f03f16a94f535e52d7f3d94658619"
+ integrity sha1-e3SLlag/A/FqlPU15S1/PZRlhhk=
+ dependencies:
+ color-convert "~0.5.0"
+
parse-json@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
@@ -6500,6 +6512,11 @@ react-native-image-crop-picker@^0.36.0:
resolved "https://registry.yarnpkg.com/react-native-image-crop-picker/-/react-native-image-crop-picker-0.36.2.tgz#fcb35c1a12d805bedbb6d94a87078e86a6c9b49f"
integrity sha512-cTauoEHHzx14ZHA7Pt65e7RVnbn1WRYQz2ufTZp9/05EtNDrdsXwNhgtXdWVFbOhYB8qgUFQjy2NelkBOHIH3g==
+react-native-image-pan-zoom@^2.1.12:
+ version "2.1.12"
+ resolved "https://registry.yarnpkg.com/react-native-image-pan-zoom/-/react-native-image-pan-zoom-2.1.12.tgz#eb98bf56fb5610379bdbfdb63219cc1baca98fd2"
+ integrity sha512-BF66XeP6dzuANsPmmFsJshM2Jyh/Mo1t8FsGc1L9Q9/sVP8MJULDabB1hms+eAoqgtyhMr5BuXV3E1hJ5U5H6Q==
+
react-native-image-picker@^4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/react-native-image-picker/-/react-native-image-picker-4.0.4.tgz#4b09d644d9ad7afaedc82db650522dca6bbefc5c"
@@ -6545,6 +6562,13 @@ react-native-markdown-package@1.8.1:
react-native-lightbox "^0.7.0"
simple-markdown "^0.7.1"
+react-native-photo-manipulator@^1.2.4:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/react-native-photo-manipulator/-/react-native-photo-manipulator-1.2.4.tgz#60da9c68c815695c43fa3ed8c77bd9f08d076158"
+ integrity sha512-EGkDxmVfom52G2jDWvY+rzzHVTlXLCfBs+IQut+VaH7TtShxQekzzqxFRr0Pch/0I5s6Vt+gjrhFZBIv7FweVg==
+ dependencies:
+ parse-color "^1.0.0"
+
react-native-picker-select@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/react-native-picker-select/-/react-native-picker-select-7.0.0.tgz#4808a1177f997e234bb8505849dfffe1a01fedac"