aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/components/comments/ZoomInCropper.tsx114
-rw-r--r--src/components/common/MomentTags.tsx44
-rw-r--r--src/components/common/NavigationIcon.tsx2
-rw-r--r--src/components/common/TaggSquareButton.tsx26
-rw-r--r--src/components/common/TaggTypeahead.tsx49
-rw-r--r--src/components/moments/CaptionScreenHeader.tsx5
-rw-r--r--src/components/moments/Moment.tsx24
-rw-r--r--src/components/moments/MomentPost.tsx2
-rw-r--r--src/components/notifications/Notification.tsx11
-rw-r--r--src/components/onboarding/MomentCategory.tsx89
-rw-r--r--src/components/profile/MomentMoreInfoDrawer.tsx1
-rw-r--r--src/constants/constants.ts1
-rw-r--r--src/constants/strings.ts1
-rw-r--r--src/routes/main/MainStackNavigator.tsx26
-rw-r--r--src/routes/main/MainStackScreen.tsx8
-rw-r--r--src/routes/tabs/NavigationBar.tsx4
-rw-r--r--src/screens/moments/CameraScreen.tsx16
-rw-r--r--src/screens/moments/TagFriendsScreen.tsx239
-rw-r--r--src/screens/profile/CaptionScreen.tsx409
-rw-r--r--src/screens/profile/CategorySelection.tsx1
-rw-r--r--src/screens/profile/ChoosingCategoryScreen.tsx192
-rw-r--r--src/screens/profile/CreateCustomCategory.tsx4
-rw-r--r--src/screens/profile/IndividualMoment.tsx2
-rw-r--r--src/screens/profile/MomentCommentsScreen.tsx5
-rw-r--r--src/screens/profile/index.ts1
-rw-r--r--src/services/MomentService.ts4
-rw-r--r--src/utils/comments.tsx9
-rw-r--r--src/utils/moments.ts84
28 files changed, 887 insertions, 486 deletions
diff --git a/src/components/comments/ZoomInCropper.tsx b/src/components/comments/ZoomInCropper.tsx
index 3a492af3..f4c06311 100644
--- a/src/components/comments/ZoomInCropper.tsx
+++ b/src/components/comments/ZoomInCropper.tsx
@@ -1,7 +1,6 @@
import {RouteProp} from '@react-navigation/core';
-import {useFocusEffect} from '@react-navigation/native';
import {StackNavigationProp} from '@react-navigation/stack';
-import React, {useCallback, useEffect, useRef, useState} from 'react';
+import React, {useEffect, useRef, useState} from 'react';
import {Image, StyleSheet, TouchableOpacity, View} from 'react-native';
import {normalize} from 'react-native-elements';
import ImageZoom, {IOnMove} from 'react-native-image-pan-zoom';
@@ -13,7 +12,6 @@ import {
HeaderHeight,
SCREEN_HEIGHT,
SCREEN_WIDTH,
- numberWithCommas,
} from '../../utils';
import {TaggSquareButton} from '../common';
import ReactNativeZoomableView from '@dudigital/react-native-zoomable-view/src/ReactNativeZoomableView';
@@ -33,7 +31,7 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({
route,
navigation,
}) => {
- const {screenType, title, media} = route.params;
+ const {screenType, media, selectedCategory} = route.params;
const [aspectRatio, setAspectRatio] = useState<number>(1);
// width and height of video, if video
const [origDimensions, setOrigDimensions] = useState<number[]>([0, 0]);
@@ -64,19 +62,6 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({
);
};
- useFocusEffect(
- useCallback(() => {
- navigation.dangerouslyGetParent()?.setOptions({
- tabBarVisible: false,
- });
- return () => {
- navigation.dangerouslyGetParent()?.setOptions({
- tabBarVisible: true,
- });
- };
- }, [navigation]),
- );
-
// Setting original aspect ratio of image
useEffect(() => {
if (media.uri && checkIfUriImage(media.uri)) {
@@ -155,11 +140,11 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({
(croppedURL: string) => {
navigation.navigate('CaptionScreen', {
screenType,
- title: title,
media: {
uri: croppedURL,
isVideo: true,
},
+ selectedCategory,
});
},
videoCrop,
@@ -169,76 +154,18 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({
// for whenever the video is altered by the user
const onVideoMove = (zoomableEvent: any) => {
- const {
- distanceBottom,
- distanceLeft,
- distanceRight,
- distanceTop,
- lastMovePinch,
- lastX,
- lastY,
- lastZoomLevel,
- offsetX,
- offsetY,
- originalHeight,
- originalWidth,
- zoomLevel,
- } = zoomableEvent;
- // let cropWidth = origDimensions[0] / (zoomLevel * aspectRatio);
- // let cropOffsetX = Math.abs((origDimensions[0] - cropWidth) / 2);
- // let screenScale = SCREEN_HEIGHT / origDimensions[1];
- // const relativeHeight = SCREEN_WIDTH / aspectRatio;
- // let cropHeight = origDimensions[1];
- // let cropOffsetY = 0;
+ const {originalHeight, originalWidth} = zoomableEvent;
let cropWidth = 0;
let cropHeight = 0;
let cropOffsetX = 0;
let cropOffsetY = 0;
- // if (aspectRatio > 1) {
- // cropWidth =
- // originalWidth * (originalWidth - (distanceLeft + distanceRight));
- // cropHeight = origDimensions[1];
- // }
-
- // console.log(relativeHeight * zoomLevel, SCREEN_HEIGHT, zoomLevel);
- // if (cropHeight * screenScale > SCREEN_HEIGHT) {
- // console.log('true');
- // } else {
- // console.log('false');
- // }
-
- // console.log(
- // aspectRatio,
- // zoomLevel,
- // originalWidth,
- // originalHeight,
- // origDimensions[0],
- // origDimensions[1],
- // cropWidth,
- // cropHeight,
- // distanceBottom,
- // distanceLeft,
- // distanceRight,
- // distanceTop,
- // SCREEN_WIDTH * zoomLevel - SCREEN_WIDTH,
- // distanceBottom + distanceTop,
- // distanceLeft + distanceRight,
- // );
- // console.log(
- // zoomLevel,
- // distanceBottom,
- // distanceLeft,
- // distanceRight,
- // distanceTop,
- // originalWidth / (originalWidth - (distanceLeft + distanceRight)),
- // );
if (vidRef !== null && vidRef.current !== null) {
vidRef.current.measure(
(
- x: number,
- y: number,
+ _x: number,
+ _y: number,
width: number,
height: number,
pageX: number,
@@ -248,11 +175,10 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({
cropWidth = origDimensions[0] * (originalWidth / width);
// offsetX
- // cropOffsetX = (origDimensions[0] - cropWidth) / 2;
cropOffsetX = -1 * origDimensions[0] * (pageX / width);
if (cropOffsetX < 0) {
cropOffsetX = 0;
- } else if (cropOffsetX + cropWidth > origDimensions[0]) {
+ } else if (cropOffsetX + cropWidth > origDimensions[0] - 1) {
cropOffsetX = origDimensions[0] - cropWidth - 1;
}
@@ -263,7 +189,11 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({
) {
const superHeight = width / aspectRatio;
cropHeight = origDimensions[1] * (originalHeight / superHeight);
- // cropOffsetY = (origDimensions[1] - cropHeight) / 2;
+
+ // offsetY
+ const topDeadZone = (height - superHeight) / 2;
+ const offsetY = topDeadZone + pageY;
+ cropOffsetY = -1 * origDimensions[1] * (offsetY / superHeight);
if (cropOffsetY < 0) {
cropOffsetY = 0;
} else if (cropOffsetY + cropHeight > origDimensions[1]) {
@@ -328,24 +258,6 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({
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> */}
-
- {/* <Image style={{ flex: 1, width: null, height: '100%' }}
- source={require('./image.jpg')}
- resizeMode="contain" /> */}
{checkIfUriImage(media.uri) ? (
<ImageZoom
style={styles.zoomView}
@@ -399,7 +311,7 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({
height: SCREEN_WIDTH / aspectRatio,
},
]}
- repeat={false}
+ repeat={true}
resizeMode={'contain'}
onLoad={(response) => {
const {width, height} = response.naturalSize;
diff --git a/src/components/common/MomentTags.tsx b/src/components/common/MomentTags.tsx
index d8a70353..37069e18 100644
--- a/src/components/common/MomentTags.tsx
+++ b/src/components/common/MomentTags.tsx
@@ -10,6 +10,13 @@ interface MomentTagsProps {
setTags: (tag: MomentTagType[]) => void;
imageRef: RefObject<Image>;
deleteFromList?: (user: ProfilePreviewType) => void;
+ boundaries?: DraggableBoundaries;
+}
+interface DraggableBoundaries {
+ top?: number;
+ bottom?: number;
+ left?: number;
+ right?: number;
}
const MomentTags: React.FC<MomentTagsProps> = ({
@@ -18,11 +25,14 @@ const MomentTags: React.FC<MomentTagsProps> = ({
setTags,
imageRef,
deleteFromList,
+ boundaries,
}) => {
const [offset, setOffset] = useState([0, 0]);
const [imageDimensions, setImageDimensions] = useState([0, 0]);
const [maxZIndex, setMaxZIndex] = useState(1);
const [draggableRefs, setDraggableRefs] = useState<RefObject<View>[]>([]);
+ // [minXBoundary, maxXBoundary, minYBoundary, maxYBoundary]
+ const [boundariesList, setBoundariesList] = useState<number[]>([0, 0, 0, 0]);
const updateTagPosition = (ref: RefObject<Image>, userId: string) => {
if (ref !== null && ref.current !== null) {
@@ -77,6 +87,24 @@ const MomentTags: React.FC<MomentTagsProps> = ({
},
);
}
+
+ // Checks for and adds boundaries
+ if (boundaries) {
+ const newBounds = [...boundariesList];
+ if (boundaries.top) {
+ newBounds[2] = boundaries.top;
+ }
+ if (boundaries.bottom) {
+ newBounds[3] = boundaries.bottom;
+ }
+ if (boundaries.left) {
+ newBounds[0] = boundaries.left;
+ }
+ if (boundaries.right) {
+ newBounds[1] = boundaries.right;
+ }
+ setBoundariesList(newBounds);
+ }
},
editing ? 100 : 0,
);
@@ -94,10 +122,10 @@ const MomentTags: React.FC<MomentTagsProps> = ({
x={(imageDimensions[0] * tag.x) / 100 + offset[0]}
y={(imageDimensions[1] * tag.y) / 100 + offset[1]}
z={tag.z}
- minX={offset[0]}
- minY={offset[1]}
- maxX={imageDimensions[0] + offset[0]}
- maxY={imageDimensions[1] + offset[1]}
+ minX={offset[0] + boundariesList[0]}
+ minY={offset[1] + boundariesList[2]}
+ maxX={imageDimensions[0] + offset[0] - boundariesList[1]}
+ maxY={imageDimensions[1] + offset[1] - boundariesList[3]}
onDragStart={() => {
const currZIndex = maxZIndex;
setMaxZIndex(currZIndex + 1);
@@ -123,10 +151,10 @@ const MomentTags: React.FC<MomentTagsProps> = ({
x={(imageDimensions[0] * tag.x) / 100 + offset[0]}
y={(imageDimensions[1] * tag.y) / 100 + offset[1]}
z={tag.z}
- minX={offset[0]}
- minY={offset[1]}
- maxX={imageDimensions[0] + offset[0]}
- maxY={imageDimensions[1] + offset[1]}
+ minX={offset[0] + boundariesList[0]}
+ minY={offset[1] + boundariesList[2]}
+ maxX={imageDimensions[0] + offset[0] - boundariesList[1]}
+ maxY={imageDimensions[1] + offset[1] - boundariesList[3]}
disabled={true}>
<TaggDraggable
draggableRef={draggableRefs[index]}
diff --git a/src/components/common/NavigationIcon.tsx b/src/components/common/NavigationIcon.tsx
index debb36b3..a5f42992 100644
--- a/src/components/common/NavigationIcon.tsx
+++ b/src/components/common/NavigationIcon.tsx
@@ -37,7 +37,7 @@ const NavigationIcon = (props: NavigationIconProps) => {
case 'Upload':
imgSrc = props.disabled
? require('../../assets/navigationIcons/new-upload.png')
- : require('../../assets/navigationIcons/upload-clicked.png');
+ : require('../../assets/navigationIcons/new-upload.png');
break;
case 'Notifications':
imgSrc = props.disabled
diff --git a/src/components/common/TaggSquareButton.tsx b/src/components/common/TaggSquareButton.tsx
index 1a1c33b3..b1e65ba6 100644
--- a/src/components/common/TaggSquareButton.tsx
+++ b/src/components/common/TaggSquareButton.tsx
@@ -1,11 +1,12 @@
-import React from 'react';
+import React, {FC} from 'react';
import {
GestureResponderEvent,
+ StyleProp,
StyleSheet,
Text,
TextStyle,
TouchableOpacity,
- ViewProps,
+ TouchableOpacityProps,
ViewStyle,
} from 'react-native';
import LinearGradient from 'react-native-linear-gradient';
@@ -15,14 +16,16 @@ import {
TAGG_PURPLE,
} from '../../constants';
import {normalize, SCREEN_WIDTH} from '../../utils';
-interface TaggSquareButtonProps extends ViewProps {
+
+interface TaggSquareButtonProps extends TouchableOpacityProps {
onPress: (event: GestureResponderEvent) => void;
title: string;
buttonStyle: 'normal' | 'large' | 'gradient';
buttonColor: 'purple' | 'white' | 'blue';
labelColor: 'white' | 'blue';
- style?: ViewStyle;
- labelStyle?: TextStyle;
+ style?: StyleProp<ViewStyle>;
+ labelStyle?: StyleProp<TextStyle>;
+ icon?: Element;
}
const TaggSquareButton: React.FC<TaggSquareButtonProps> = (props) => {
@@ -50,8 +53,10 @@ const TaggSquareButton: React.FC<TaggSquareButtonProps> = (props) => {
case 'large':
return (
<TouchableOpacity
+ {...props}
onPress={props.onPress}
style={[styles.largeButton, buttonColor, props.style]}>
+ {props.icon}
<Text style={[styles.largeLabel, labelColor, props.labelStyle]}>
{props.title}
</Text>
@@ -59,12 +64,16 @@ const TaggSquareButton: React.FC<TaggSquareButtonProps> = (props) => {
);
case 'gradient':
return (
- <TouchableOpacity onPress={props.onPress} style={props.style}>
+ <TouchableOpacity
+ {...props}
+ onPress={props.onPress}
+ style={props.style}>
<LinearGradient
style={styles.gradientButton}
colors={BACKGROUND_GRADIENT_MAP[0]}
useAngle
angle={90}>
+ {props.icon}
<Text style={[styles.gradientLabel, props.labelStyle]}>
{props.title}
</Text>
@@ -75,8 +84,10 @@ const TaggSquareButton: React.FC<TaggSquareButtonProps> = (props) => {
default:
return (
<TouchableOpacity
+ {...props}
onPress={props.onPress}
style={[styles.normalButton, buttonColor, props.style]}>
+ {props.icon}
<Text style={[styles.normalLabel, labelColor, props.labelStyle]}>
{props.title}
</Text>
@@ -87,6 +98,7 @@ const TaggSquareButton: React.FC<TaggSquareButtonProps> = (props) => {
const styles = StyleSheet.create({
largeButton: {
+ flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
width: '70%',
@@ -99,6 +111,7 @@ const styles = StyleSheet.create({
color: '#eee',
},
normalButton: {
+ flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
width: SCREEN_WIDTH * 0.45,
@@ -111,6 +124,7 @@ const styles = StyleSheet.create({
fontWeight: '500',
},
gradientButton: {
+ flexDirection: 'row',
marginTop: '8%',
borderRadius: 5,
paddingVertical: '5%',
diff --git a/src/components/common/TaggTypeahead.tsx b/src/components/common/TaggTypeahead.tsx
index 7967fdbc..672cff69 100644
--- a/src/components/common/TaggTypeahead.tsx
+++ b/src/components/common/TaggTypeahead.tsx
@@ -1,27 +1,34 @@
-import React, {Fragment, useEffect, useState} from 'react';
-import {ScrollView, StyleSheet, View} from 'react-native';
+import React, {Fragment, useEffect, useRef, useState} from 'react';
+import {LayoutChangeEvent, 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_HEIGHT, SCREEN_WIDTH, shuffle} from '../../utils';
+import {isIPhoneX, SCREEN_HEIGHT, SCREEN_WIDTH, shuffle} from '../../utils';
import TaggUserRowCell from './TaggUserRowCell';
type TaggTypeaheadProps = {
keyword: string | undefined;
component: string | undefined;
onSuggestionPress: (suggestion: Suggestion) => void;
+ isShowBelowStyle?: boolean;
};
const TaggTypeahead: React.FC<TaggTypeaheadProps> = ({
keyword,
component,
onSuggestionPress,
+ isShowBelowStyle = false,
}) => {
const {friends} = useSelector((state: RootState) => state.friends);
const [results, setResults] = useState<ProfilePreviewType[]>([]);
+ const [viewPxy, setViewPxy] = useState<{px: number; py: number}>({
+ px: 0,
+ py: 0,
+ });
+ const viewRef = useRef<View>(null);
const [height, setHeight] = useState(0);
const margin = component === 'comment' ? -10 : 0;
@@ -33,6 +40,22 @@ const TaggTypeahead: React.FC<TaggTypeaheadProps> = ({
}
}, [keyword]);
+ const onLayout = (e: LayoutChangeEvent) => {
+ setHeight(e.nativeEvent.layout.height);
+ viewRef.current?.measure(
+ (
+ _fx: number,
+ _fy: number,
+ _width: number,
+ _height: number,
+ px: number,
+ py: number,
+ ) => {
+ setViewPxy({px, py});
+ },
+ );
+ };
+
const getQuerySuggested = async () => {
if (keyword === undefined || keyword === '@') {
return;
@@ -50,14 +73,17 @@ const TaggTypeahead: React.FC<TaggTypeaheadProps> = ({
}
return (
- <View>
- <View style={styles.overlay} />
+ <View ref={viewRef} onLayout={onLayout}>
+ {/* <View ref={viewRef} onLayout={onLayout}> */}
+ {!isShowBelowStyle && <View style={styles.overlay} />}
<ScrollView
- style={[styles.container, {top: -height, margin: margin}]}
+ style={[
+ styles.container,
+ isShowBelowStyle
+ ? [styles.topPadding, {left: -viewPxy.px}]
+ : {top: -height, margin: margin},
+ ]}
showsVerticalScrollIndicator={false}
- onLayout={(event) => {
- setHeight(event.nativeEvent.layout.height);
- }}
keyboardShouldPersistTaps={'always'}>
{results.map((user) => (
<TaggUserRowCell
@@ -78,10 +104,10 @@ const TaggTypeahead: React.FC<TaggTypeaheadProps> = ({
const styles = StyleSheet.create({
container: {
+ position: 'absolute',
width: SCREEN_WIDTH,
maxHeight: 264,
backgroundColor: 'white',
- position: 'absolute',
alignSelf: 'center',
zIndex: 1,
},
@@ -95,6 +121,9 @@ const styles = StyleSheet.create({
bottom: 10,
zIndex: -1,
},
+ topPadding: {
+ top: isIPhoneX() ? 180 : 150,
+ },
});
export default TaggTypeahead;
diff --git a/src/components/moments/CaptionScreenHeader.tsx b/src/components/moments/CaptionScreenHeader.tsx
index 0638c128..cda85e57 100644
--- a/src/components/moments/CaptionScreenHeader.tsx
+++ b/src/components/moments/CaptionScreenHeader.tsx
@@ -1,5 +1,6 @@
import React from 'react';
import {Text, View, StyleSheet, ViewProps} from 'react-native';
+import {normalize} from '../../utils';
interface CaptionScreenHeaderProps extends ViewProps {
title: string;
}
@@ -26,8 +27,8 @@ const styles = StyleSheet.create({
width: '90%',
},
header: {
- fontSize: 20,
- fontWeight: 'bold',
+ fontSize: normalize(18),
+ fontWeight: '700',
color: 'white',
textAlign: 'center',
},
diff --git a/src/components/moments/Moment.tsx b/src/components/moments/Moment.tsx
index 73503c5e..087b343f 100644
--- a/src/components/moments/Moment.tsx
+++ b/src/components/moments/Moment.tsx
@@ -7,7 +7,6 @@ import LinearGradient from 'react-native-linear-gradient';
import DeleteIcon from '../../assets/icons/delete-logo.svg';
import DownIcon from '../../assets/icons/down_icon.svg';
import BigPlusIcon from '../../assets/icons/plus-icon-white.svg';
-import PlusIcon from '../../assets/icons/plus-icon.svg';
import UpIcon from '../../assets/icons/up_icon.svg';
import {TAGG_LIGHT_BLUE} from '../../constants';
import {MomentType, ScreenType} from '../../types';
@@ -40,14 +39,6 @@ const Moment: React.FC<MomentProps> = ({
externalStyles,
}) => {
const navigation = useNavigation();
-
- const navigateToCameraScreen = () => {
- navigation.navigate('CameraScreen', {
- title,
- screenType,
- });
- };
-
return (
<View style={[styles.container, externalStyles?.container]}>
<View style={[styles.header, externalStyles?.header]}>
@@ -81,13 +72,6 @@ const Moment: React.FC<MomentProps> = ({
)}
{!userXId && (
<View style={styles.row}>
- <PlusIcon
- width={23}
- height={23}
- onPress={navigateToCameraScreen}
- color={TAGG_LIGHT_BLUE}
- style={styles.horizontalMargin}
- />
{shouldAllowDeletion && (
<DeleteIcon
onPress={() => handleMomentCategoryDelete(title)}
@@ -114,7 +98,13 @@ const Moment: React.FC<MomentProps> = ({
/>
))}
{(images === undefined || images.length === 0) && !userXId && (
- <TouchableOpacity onPress={navigateToCameraScreen}>
+ <TouchableOpacity
+ onPress={() =>
+ navigation.navigate('CameraScreen', {
+ screenType: ScreenType.Profile,
+ selectedCategory: title,
+ })
+ }>
<LinearGradient
colors={['rgba(105, 141, 211, 1)', 'rgba(105, 141, 211, 0.3)']}>
<View style={styles.defaultImage}>
diff --git a/src/components/moments/MomentPost.tsx b/src/components/moments/MomentPost.tsx
index 319542f9..07295369 100644
--- a/src/components/moments/MomentPost.tsx
+++ b/src/components/moments/MomentPost.tsx
@@ -2,6 +2,7 @@ import {useNavigation} from '@react-navigation/native';
import React, {useContext, useEffect, useMemo, useRef, useState} from 'react';
import {
Image,
+ Keyboard,
KeyboardAvoidingView,
Platform,
StatusBar,
@@ -252,6 +253,7 @@ const MomentPost: React.FC<MomentPostProps> = ({
onPress={() => {
setVisible(!visible);
setFadeValue(new Animated.Value(0));
+ Keyboard.dismiss();
}}>
<View style={styles.contentContainer}>
<View style={styles.topContainer}>
diff --git a/src/components/notifications/Notification.tsx b/src/components/notifications/Notification.tsx
index fd1b11ac..946f2164 100644
--- a/src/components/notifications/Notification.tsx
+++ b/src/components/notifications/Notification.tsx
@@ -314,7 +314,11 @@ const Notification: React.FC<NotificationProps> = (props) => {
<TouchableWithoutFeedback
style={styles.moment}
onPress={onNotificationTap}>
- <Image style={styles.imageFlex} source={{uri: momentURI}} />
+ <Image
+ style={styles.imageFlex}
+ source={{uri: momentURI}}
+ resizeMode={'contain'}
+ />
</TouchableWithoutFeedback>
)}
</View>
@@ -357,8 +361,9 @@ const styles = StyleSheet.create({
lineHeight: normalize(14.32),
},
moment: {
- height: normalize(42),
- width: normalize(42),
+ height: normalize(72),
+ width: normalize(40),
+ backgroundColor: 'black',
},
buttonsContainer: {
height: '80%',
diff --git a/src/components/onboarding/MomentCategory.tsx b/src/components/onboarding/MomentCategory.tsx
index 97099b9e..eb9d5bcc 100644
--- a/src/components/onboarding/MomentCategory.tsx
+++ b/src/components/onboarding/MomentCategory.tsx
@@ -3,11 +3,12 @@ import {StyleSheet} from 'react-native';
import {Image, Text} from 'react-native-animatable';
import {TouchableOpacity} from 'react-native-gesture-handler';
import LinearGradient from 'react-native-linear-gradient';
+import {BACKGROUND_GRADIENT_MAP} from '../../constants';
import {
- BACKGROUND_GRADIENT_MAP,
- MOMENT_CATEGORY_BG_COLORS,
-} from '../../constants';
-import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
+ getMomentCategoryIconInfo,
+ SCREEN_HEIGHT,
+ SCREEN_WIDTH,
+} from '../../utils';
type MomentCategoryProps = {
categoryType: string;
@@ -22,85 +23,7 @@ const MomentCategory: React.FC<MomentCategoryProps> = ({
isAdded,
onSelect,
}) => {
- var icon, bgColor;
-
- /**
- * Choose icon and color based on category type
- */
- switch (categoryType) {
- case 'Friends':
- icon = require('../../assets/moment-categories/friends-icon.png');
- bgColor = MOMENT_CATEGORY_BG_COLORS[0];
- break;
- case 'Adventure':
- icon = require('../../assets/moment-categories/adventure-icon.png');
- bgColor = MOMENT_CATEGORY_BG_COLORS[1];
- break;
- case 'Photo Dump':
- icon = require('../../assets/moment-categories/photo-dump-icon.png');
- bgColor = MOMENT_CATEGORY_BG_COLORS[2];
- break;
- case 'Food':
- icon = require('../../assets/moment-categories/food-icon.png');
- bgColor = MOMENT_CATEGORY_BG_COLORS[3];
- break;
- case 'Music':
- icon = require('../../assets/moment-categories/music-icon.png');
- bgColor = MOMENT_CATEGORY_BG_COLORS[4];
- break;
- case 'Art':
- icon = require('../../assets/moment-categories/art-icon.png');
- bgColor = MOMENT_CATEGORY_BG_COLORS[5];
- break;
- case 'Sports':
- icon = require('../../assets/moment-categories/sports-icon.png');
- bgColor = MOMENT_CATEGORY_BG_COLORS[6];
- break;
- case 'Fashion':
- icon = require('../../assets/moment-categories/fashion-icon.png');
- bgColor = MOMENT_CATEGORY_BG_COLORS[7];
- break;
- case 'Travel':
- icon = require('../../assets/moment-categories/travel-icon.png');
- bgColor = MOMENT_CATEGORY_BG_COLORS[8];
- break;
- case 'Pets':
- icon = require('../../assets/moment-categories/pets-icon.png');
- bgColor = MOMENT_CATEGORY_BG_COLORS[9];
- break;
- case 'Fitness':
- icon = require('../../assets/moment-categories/fitness-icon.png');
- bgColor = MOMENT_CATEGORY_BG_COLORS[10];
- break;
- case 'DIY':
- icon = require('../../assets/moment-categories/diy-icon.png');
- bgColor = MOMENT_CATEGORY_BG_COLORS[11];
- break;
- case 'Nature':
- icon = require('../../assets/moment-categories/nature-icon.png');
- bgColor = MOMENT_CATEGORY_BG_COLORS[12];
- break;
- case 'Early Life':
- icon = require('../../assets/moment-categories/early-life-icon.png');
- bgColor = MOMENT_CATEGORY_BG_COLORS[13];
- break;
- case 'Beauty':
- icon = require('../../assets/moment-categories/beauty-icon.png');
- bgColor = MOMENT_CATEGORY_BG_COLORS[14];
- break;
- default:
- // All custom categories
- icon = require('../../assets/moment-categories/custom-icon.png');
- // A quick deterministic "random" color picker by summing up ascii char codees
- const charCodeSum = categoryType
- .split('')
- .reduce((acc: number, x: string) => acc + x.charCodeAt(0), 0);
- bgColor =
- MOMENT_CATEGORY_BG_COLORS[
- charCodeSum % MOMENT_CATEGORY_BG_COLORS.length
- ];
- break;
- }
+ const {icon, bgColor} = getMomentCategoryIconInfo(categoryType);
/**
* The Linear Gradient helps us add a gradient border when the category is already added /selected by user
diff --git a/src/components/profile/MomentMoreInfoDrawer.tsx b/src/components/profile/MomentMoreInfoDrawer.tsx
index 910aa095..493f6238 100644
--- a/src/components/profile/MomentMoreInfoDrawer.tsx
+++ b/src/components/profile/MomentMoreInfoDrawer.tsx
@@ -129,6 +129,7 @@ const MomentMoreInfoDrawer: React.FC<MomentMoreInfoDrawerProps> = (props) => {
screenType: screenType,
selectedTags: tags,
moment: moment,
+ selectedCategory: moment.moment_category,
});
};
diff --git a/src/constants/constants.ts b/src/constants/constants.ts
index f4ffd750..13a73208 100644
--- a/src/constants/constants.ts
+++ b/src/constants/constants.ts
@@ -66,6 +66,7 @@ export const YOUTUBE_FONT_COLOR: string = '#FCA4A4';
export const TAGG_PURPLE = '#8F01FF';
export const TAGG_DARK_BLUE = '#4E699C';
+export const TAGG_DARK_PURPLEISH_BLUE = '#4755A1';
export const TAGG_LIGHT_BLUE: string = '#698DD3';
export const TAGG_LIGHT_BLUE_2: string = '#6EE7E7';
export const TAGG_LIGHT_PURPLE = '#F4DDFF';
diff --git a/src/constants/strings.ts b/src/constants/strings.ts
index 112bc546..071b3835 100644
--- a/src/constants/strings.ts
+++ b/src/constants/strings.ts
@@ -45,6 +45,7 @@ export const ERROR_SELECT_GENDER = 'Please select your gender';
export const ERROR_SELECT_UNIVERSITY = 'Please select your University';
export const ERROR_SERVER_DOWN = 'mhm, looks like our servers are down, please refresh and try again in a few mins';
export const ERROR_SOMETHING_WENT_WRONG = 'Oh dear, don’t worry someone will be held responsible for this error, In the meantime refresh the app';
+export const ERROR_NO_MOMENT_CATEGORY = 'Please select a category!';
export const ERROR_SOMETHING_WENT_WRONG_REFRESH = "Ha, looks like this one's on us, please refresh and try again";
export const ERROR_SOMETHING_WENT_WRONG_RELOAD = "You broke it, Just kidding! we don't know what happened... Please reload the app and try again";
export const ERROR_T_AND_C_NOT_ACCEPTED = 'You must first agree to the terms and conditions.';
diff --git a/src/routes/main/MainStackNavigator.tsx b/src/routes/main/MainStackNavigator.tsx
index bc023da8..c569d2d6 100644
--- a/src/routes/main/MainStackNavigator.tsx
+++ b/src/routes/main/MainStackNavigator.tsx
@@ -35,13 +35,25 @@ export type MainStackParams = {
userXId: string | undefined;
screenType: ScreenType;
};
+ CameraScreen: {
+ screenType: ScreenType;
+ selectedCategory?: string;
+ };
+ ZoomInCropper: {
+ media: {uri: string; isVideo: boolean};
+ screenType: ScreenType;
+ selectedCategory?: string;
+ };
CaptionScreen: {
- title?: string;
- media?: {uri: string; isVideo: boolean};
screenType: ScreenType;
+ media?: {uri: string; isVideo: boolean};
+ selectedCategory?: string;
selectedTags?: MomentTagType[];
moment?: MomentType;
};
+ ChoosingCategoryScreen: {
+ newCustomCategory?: string;
+ };
TagFriendsScreen: {
media: {
uri: string;
@@ -80,6 +92,7 @@ export type MainStackParams = {
};
CreateCustomCategory: {
existingCategories: string[];
+ fromScreen: 'ChoosingCategoryScreen' | 'CategorySelection';
};
Notifications: {
screenType: ScreenType;
@@ -109,15 +122,6 @@ export type MainStackParams = {
ChatList: undefined;
Chat: undefined;
NewChatModal: undefined;
- ZoomInCropper: {
- media: {uri: string; isVideo: boolean};
- screenType: ScreenType;
- title: string;
- };
- CameraScreen: {
- title: string;
- screenType: ScreenType;
- };
};
export const MainStack = createStackNavigator<MainStackParams>();
diff --git a/src/routes/main/MainStackScreen.tsx b/src/routes/main/MainStackScreen.tsx
index 948f37b8..15300c0d 100644
--- a/src/routes/main/MainStackScreen.tsx
+++ b/src/routes/main/MainStackScreen.tsx
@@ -40,6 +40,7 @@ import {ScreenType} from '../../types';
import {AvatarHeaderHeight, ChatHeaderHeight, SCREEN_WIDTH} from '../../utils';
import {MainStack, MainStackParams} from './MainStackNavigator';
import {ZoomInCropper} from '../../components/comments/ZoomInCropper';
+import ChoosingCategoryScreen from '../../screens/profile/ChoosingCategoryScreen';
/**
* 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.
@@ -181,6 +182,13 @@ const MainStackScreen: React.FC<MainStackProps> = ({route}) => {
}}
/>
<MainStack.Screen
+ name="ChoosingCategoryScreen"
+ component={ChoosingCategoryScreen}
+ options={{
+ ...headerBarOptions('white', 'Categories'),
+ }}
+ />
+ <MainStack.Screen
name="SocialMediaTaggs"
component={SocialMediaTaggs}
initialParams={{screenType}}
diff --git a/src/routes/tabs/NavigationBar.tsx b/src/routes/tabs/NavigationBar.tsx
index 12f6ab58..c3f0b9f8 100644
--- a/src/routes/tabs/NavigationBar.tsx
+++ b/src/routes/tabs/NavigationBar.tsx
@@ -66,7 +66,7 @@ const NavigationBar: React.FC = () => {
disabled={!focused}
/>
);
- case 'Profile':
+ case 'ProfileTab':
return <NavigationIcon tab="Profile" disabled={!focused} />;
case 'SuggestedPeople':
return (
@@ -118,7 +118,7 @@ const NavigationBar: React.FC = () => {
}}
/>
<Tabs.Screen
- name="Profile"
+ name="ProfileTab"
component={MainStackScreen}
initialParams={{screenType: ScreenType.Profile}}
/>
diff --git a/src/screens/moments/CameraScreen.tsx b/src/screens/moments/CameraScreen.tsx
index fa1bdd10..4ed984e7 100644
--- a/src/screens/moments/CameraScreen.tsx
+++ b/src/screens/moments/CameraScreen.tsx
@@ -30,7 +30,7 @@ interface CameraScreenProps {
navigation: CameraScreenNavigationProps;
}
const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => {
- const {title, screenType} = route.params;
+ const {screenType, selectedCategory} = route.params;
const cameraRef = createRef<RNCamera>();
const tabBarHeight = useBottomTabBarHeight();
const [cameraType, setCameraType] = useState<keyof CameraType>('front');
@@ -45,11 +45,6 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => {
navigation.dangerouslyGetParent()?.setOptions({
tabBarVisible: false,
});
- return () => {
- navigation.dangerouslyGetParent()?.setOptions({
- tabBarVisible: true,
- });
- };
}, [navigation]),
);
@@ -72,18 +67,17 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => {
const navigateToCropper = (uri: string) => {
navigation.navigate('ZoomInCropper', {
screenType,
- title,
media: {
uri,
isVideo: false,
},
+ selectedCategory,
});
};
const navigateToCaptionScreen = (isVideo: boolean, uri: string) => {
navigation.navigate('CaptionScreen', {
screenType,
- title,
media: {
uri,
isVideo,
@@ -101,6 +95,9 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => {
setShowSaveButton(false);
setMediaFromGallery('');
} else {
+ navigation.dangerouslyGetParent()?.setOptions({
+ tabBarVisible: true,
+ });
navigation.goBack();
}
};
@@ -116,6 +113,9 @@ const CameraScreen: React.FC<CameraScreenProps> = ({route, navigation}) => {
style={styles.camera}
type={cameraType}
flashMode={flashMode}
+ onDoubleTap={() => {
+ setCameraType(cameraType === 'front' ? 'back' : 'front');
+ }}
/>
<View style={[styles.bottomContainer, {bottom: tabBarHeight}]}>
{showSaveButton ? (
diff --git a/src/screens/moments/TagFriendsScreen.tsx b/src/screens/moments/TagFriendsScreen.tsx
index 201caf49..fc3bccf2 100644
--- a/src/screens/moments/TagFriendsScreen.tsx
+++ b/src/screens/moments/TagFriendsScreen.tsx
@@ -1,19 +1,27 @@
import {RouteProp} from '@react-navigation/core';
import {useNavigation} from '@react-navigation/native';
import React, {useEffect, useRef, useState} from 'react';
-import {Image, StyleSheet, TouchableWithoutFeedback, View} from 'react-native';
-import {Button} from 'react-native-elements';
+import {
+ Image,
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+ TouchableWithoutFeedback,
+ View,
+} from 'react-native';
import Video from 'react-native-video';
import {MainStackParams} from 'src/routes';
-import {
- CaptionScreenHeader,
- MomentTags,
- SearchBackground,
-} from '../../components';
+import BackArrow from '../../assets/icons/back-arrow.svg';
+import {MomentTags} from '../../components';
import {TagFriendsFooter} from '../../components/moments';
-import {TAGG_LIGHT_BLUE_2} from '../../constants';
import {MomentTagType} from '../../types';
-import {SCREEN_WIDTH, StatusBarHeight} from '../../utils';
+import {
+ HeaderHeight,
+ isIPhoneX,
+ normalize,
+ SCREEN_HEIGHT,
+ SCREEN_WIDTH,
+} from '../../utils';
type TagFriendsScreenRouteProps = RouteProp<
MainStackParams,
@@ -29,6 +37,9 @@ const TagFriendsScreen: React.FC<TagFriendsScreenProps> = ({route}) => {
const [tags, setTags] = useState<MomentTagType[]>([]);
const [imageWidth, setImageWidth] = useState<number>(0);
const [imageHeight, setImageHeight] = useState<number>(0);
+ const [bottomBound, setBottomBound] = useState<number>(0);
+ const [topHeight, setTopHeight] = useState<number>(0);
+ const [topBound, setTopBound] = useState<number>(0);
/*
* Update list of tagged users from route params
@@ -41,28 +52,57 @@ const TagFriendsScreen: React.FC<TagFriendsScreenProps> = ({route}) => {
* Navigate back to Tag Users Screen, send selected users
*/
const handleDone = () => {
- navigation.navigate('CaptionScreen', {
- ...route.params,
- selectedTags: tags,
- });
+ // Makes sure that this can only be pressed if there are tags
+ if (tags.length !== 0) {
+ navigation.navigate('CaptionScreen', {
+ ...route.params,
+ selectedTags: tags,
+ });
+ }
};
const setMediaDimensions = (width: number, height: number) => {
const imageAspectRatio = width / height;
+ const screenRatio = SCREEN_WIDTH / SCREEN_HEIGHT;
- // aspectRatio: >= 1 [Landscape] [1:1]
- if (imageAspectRatio >= 1) {
+ // aspectRatio is wider than or equal to screen
+ if (imageAspectRatio >= screenRatio) {
setImageWidth(SCREEN_WIDTH);
setImageHeight(SCREEN_WIDTH / imageAspectRatio);
}
- // aspectRatio: < 1 [Portrait]
- if (imageAspectRatio < 1) {
- setImageHeight(SCREEN_WIDTH);
- setImageWidth(SCREEN_WIDTH * imageAspectRatio);
+ // aspectRatio is taller than screen
+ if (imageAspectRatio < screenRatio) {
+ setImageHeight(SCREEN_HEIGHT);
+ setImageWidth(SCREEN_HEIGHT * imageAspectRatio);
}
};
/*
+ * Calculate boundary (if any) for drag from bottom and top
+ */
+ useEffect(() => {
+ // Bottom dead zone
+ if (SCREEN_HEIGHT / 2 - imageHeight / 2 < SCREEN_HEIGHT * 0.15) {
+ if (SCREEN_HEIGHT / 2 - imageHeight / 2 < 0) {
+ setBottomBound(SCREEN_HEIGHT * 0.15);
+ } else {
+ setBottomBound(
+ SCREEN_HEIGHT * 0.15 - (SCREEN_HEIGHT / 2 - imageHeight / 2),
+ );
+ }
+ }
+
+ // Top dead zone
+ if (SCREEN_HEIGHT / 2 - imageHeight / 2 < topHeight) {
+ if (SCREEN_HEIGHT / 2 - imageHeight / 2 < 0) {
+ setTopBound(topHeight + 15);
+ } else {
+ setTopBound(topHeight - (SCREEN_HEIGHT / 2 - imageHeight / 2) + 15);
+ }
+ }
+ }, [imageHeight, imageWidth, topHeight]);
+
+ /*
* Calculating image width and height with respect to it's enclosing view's dimensions. Only works for images.
*/
useEffect(() => {
@@ -78,25 +118,12 @@ const TagFriendsScreen: React.FC<TagFriendsScreenProps> = ({route}) => {
}, []);
return (
- <SearchBackground>
- <View style={styles.contentContainer}>
- <View style={styles.buttonsContainer}>
- <Button
- title="Cancel"
- buttonStyle={styles.button}
- onPress={() => navigation.goBack()}
- />
- <Button
- title="Done"
- titleStyle={styles.shareButtonTitle}
- buttonStyle={styles.button}
- onPress={handleDone}
- />
- </View>
- <CaptionScreenHeader
- style={styles.header}
- title={'Tap on photo to tag friends!'}
- />
+ <View style={styles.contentContainer}>
+ <View
+ style={[
+ styles.innerContainer,
+ {paddingTop: SCREEN_HEIGHT / 2 - imageHeight / 2},
+ ]}>
<TouchableWithoutFeedback
onPress={() =>
navigation.navigate('TagSelectionScreen', {
@@ -108,7 +135,6 @@ const TagFriendsScreen: React.FC<TagFriendsScreenProps> = ({route}) => {
style={{
width: imageWidth,
height: imageHeight,
- marginVertical: (SCREEN_WIDTH - imageHeight) / 2,
marginHorizontal: (SCREEN_WIDTH - imageWidth) / 2,
}}
ref={imageRef}>
@@ -136,55 +162,142 @@ const TagFriendsScreen: React.FC<TagFriendsScreenProps> = ({route}) => {
style={{
width: imageWidth,
height: imageHeight,
- marginVertical: (SCREEN_WIDTH - imageHeight) / 2,
marginHorizontal: (SCREEN_WIDTH - imageWidth) / 2,
}}
source={{uri: media.uri}}
/>
)}
</TouchableWithoutFeedback>
- {tags.length !== 0 && (
- <MomentTags
- tags={tags}
- setTags={setTags}
- editing={true}
- imageRef={imageRef}
- deleteFromList={(user) =>
- setTags(tags.filter((tag) => tag.user.id !== user.id))
- }
- />
- )}
+ </View>
+ <View
+ style={styles.titleContainer}
+ onLayout={(event) => {
+ const {y, height} = event.nativeEvent.layout;
+ setTopHeight(y + height);
+ }}>
+ <TouchableOpacity
+ onPress={() => {
+ navigation.goBack();
+ }}
+ style={styles.backArrow}>
+ <View style={styles.backArrowContainer}>
+ <BackArrow
+ height={normalize(18)}
+ width={normalize(18)}
+ color={'white'}
+ />
+ </View>
+ </TouchableOpacity>
+ <TouchableWithoutFeedback style={styles.captionContainer}>
+ {tags.length === 0 ? (
+ <Text style={styles.header}>Tap on photo to tag friends!</Text>
+ ) : (
+ <Text style={styles.header}>Press and drag to move</Text>
+ )}
+ </TouchableWithoutFeedback>
+ <TouchableOpacity
+ style={styles.buttonContainer}
+ // Altering the opacity style of TouchableOpacity doesn't work,
+ // so the next two lines are needed
+ disabled={tags.length === 0}
+ activeOpacity={tags.length === 0 ? 0 : 1}
+ onPress={handleDone}>
+ <Text
+ style={[
+ styles.shareButtonTitle,
+ // makes the Done buttomn invisible if there are no tags
+ // eslint-disable-next-line react-native/no-inline-styles
+ {opacity: tags.length !== 0 ? 1 : 0},
+ ]}>
+ Done
+ </Text>
+ </TouchableOpacity>
+ </View>
+ {tags.length !== 0 && (
+ <MomentTags
+ tags={tags}
+ setTags={setTags}
+ editing={true}
+ imageRef={imageRef}
+ deleteFromList={(user) =>
+ setTags(tags.filter((tag) => tag.user.id !== user.id))
+ }
+ boundaries={{top: topBound, bottom: bottomBound}}
+ />
+ )}
+ {tags.length !== 0 && (
<View style={styles.footerContainer}>
<TagFriendsFooter tags={tags} setTags={setTags} />
</View>
- </View>
- </SearchBackground>
+ )}
+ </View>
);
};
const styles = StyleSheet.create({
contentContainer: {
- paddingTop: StatusBarHeight,
+ backgroundColor: 'black',
+ height: SCREEN_HEIGHT,
+ alignContent: 'center',
},
- buttonsContainer: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- marginLeft: '5%',
- marginRight: '5%',
+ backArrow: {
+ marginTop: isIPhoneX() ? HeaderHeight : '10%',
+ zIndex: 9999,
+ },
+ backArrowContainer: {
+ flex: 1,
+ flexDirection: 'column',
+ justifyContent: 'center',
+ alignContent: 'center',
},
button: {
- backgroundColor: 'transparent',
+ zIndex: 9999,
+ },
+ buttonContainer: {
+ marginTop: isIPhoneX() ? HeaderHeight : '10%',
+ right: 0,
+ zIndex: 9999,
+ flexDirection: 'row',
+ justifyContent: 'flex-end',
+ },
+ captionContainer: {
+ width: SCREEN_WIDTH,
+ flexDirection: 'row',
+ justifyContent: 'center',
+ zIndex: 9999,
},
shareButtonTitle: {
fontWeight: 'bold',
- color: TAGG_LIGHT_BLUE_2,
+ fontSize: 18,
+ color: 'white',
+ },
+ titleContainer: {
+ flexDirection: 'row',
+ justifyContent: 'space-evenly',
+ alignContent: 'center',
+ zIndex: 9999,
},
header: {
- marginVertical: 20,
+ marginTop: isIPhoneX() ? HeaderHeight : '10%',
+ fontSize: 18,
+ fontWeight: 'bold',
+ color: 'white',
+ textAlign: 'center',
},
footerContainer: {
- marginHorizontal: '5%',
marginTop: '3%',
+ backgroundColor: 'black',
+ padding: '5%',
+ flexDirection: 'column',
+ justifyContent: 'center',
+ width: SCREEN_WIDTH,
+ position: 'absolute',
+ paddingBottom: 0,
+ bottom: 0,
+ height: SCREEN_HEIGHT * 0.15,
+ },
+ innerContainer: {
+ position: 'absolute',
},
});
diff --git a/src/screens/profile/CaptionScreen.tsx b/src/screens/profile/CaptionScreen.tsx
index 912865ae..38c6d8f0 100644
--- a/src/screens/profile/CaptionScreen.tsx
+++ b/src/screens/profile/CaptionScreen.tsx
@@ -1,9 +1,10 @@
import {RouteProp} from '@react-navigation/native';
import {StackNavigationProp} from '@react-navigation/stack';
-import React, {Fragment, useEffect, useState} from 'react';
+import React, {FC, useEffect, useMemo, useState} from 'react';
import {
Alert,
Image,
+ ImageSourcePropType,
Keyboard,
KeyboardAvoidingView,
Platform,
@@ -13,20 +14,25 @@ import {
TouchableWithoutFeedback,
View,
} from 'react-native';
-import {MentionInputControlled} from '../../components';
-import {Button, normalize} from 'react-native-elements';
+import {Button} from 'react-native-elements';
import Video from 'react-native-video';
import {useDispatch, useSelector} from 'react-redux';
import FrontArrow from '../../assets/icons/front-arrow.svg';
-import {SearchBackground} from '../../components';
+import {
+ MentionInputControlled,
+ SearchBackground,
+ TaggSquareButton,
+} from '../../components';
import {CaptionScreenHeader} from '../../components/';
import TaggLoadingIndicator from '../../components/common/TaggLoadingIndicator';
import {TAGG_LIGHT_BLUE_2} from '../../constants';
import {
+ ERROR_NO_MOMENT_CATEGORY,
ERROR_SOMETHING_WENT_WRONG_REFRESH,
ERROR_UPLOAD,
SUCCESS_PIC_UPLOAD,
} from '../../constants/strings';
+import * as RootNavigation from '../../RootNavigation';
import {MainStackParams} from '../../routes';
import {
handlePresignedURL,
@@ -41,7 +47,7 @@ import {
} from '../../store/actions';
import {RootState} from '../../store/rootReducer';
import {MomentTagType} from '../../types';
-import {SCREEN_WIDTH, StatusBarHeight} from '../../utils';
+import {isIPhoneX, normalize, SCREEN_WIDTH, StatusBarHeight} from '../../utils';
import {mentionPartTypes} from '../../utils/comments';
import {Trimmer, VideoPlayer} from 'react-native-video-processing';
@@ -49,28 +55,29 @@ import {Trimmer, VideoPlayer} from 'react-native-video-processing';
* Upload Screen to allow users to upload posts to Tagg
*/
type CaptionScreenRouteProp = RouteProp<MainStackParams, 'CaptionScreen'>;
+
type CaptionScreenNavigationProp = StackNavigationProp<
MainStackParams,
'CaptionScreen'
>;
+
interface CaptionScreenProps {
route: CaptionScreenRouteProp;
navigation: CaptionScreenNavigationProp;
}
const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => {
- const {title, screenType, selectedTags, moment} = route.params;
+ // moment is only present when editing
+ const {moment} = route.params;
const {
user: {userId},
} = useSelector((state: RootState) => state.user);
const dispatch = useDispatch();
const [caption, setCaption] = useState(moment ? moment.caption : '');
const [loading, setLoading] = useState(false);
- const [tags, setTags] = useState<MomentTagType[]>(
- selectedTags ? selectedTags : [],
- );
- const [aspectRatio, setAspectRatio] = useState<number>(1);
- const [taggedList, setTaggedList] = useState<string>('');
+ const [tags, setTags] = useState<MomentTagType[]>([]);
+ const [taggedUsersText, setTaggedUsersText] = useState('');
+ const [momentCategory, setMomentCategory] = useState<string | undefined>();
const mediaUri = moment ? moment.moment_url : route.params.media!.uri;
// console.log('mediaUri', mediaUri);
// TODO: change this once moment refactor is done
@@ -84,48 +91,68 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => {
: route.params.media?.isVideo ?? false;
useEffect(() => {
- setTags(selectedTags ? selectedTags : []);
- }, [selectedTags]);
+ setTags(route.params.selectedTags ?? []);
+ }, [route.params.selectedTags]);
useEffect(() => {
- const getTaggedUsersListString = () => {
- let listString = '';
- for (let i = 0; i < tags.length; i++) {
- if (listString.length < 21) {
- listString = listString.concat(`@${tags[i].user.username} `);
- } else {
- break;
- }
+ setMomentCategory(route.params.selectedCategory);
+ }, [route.params.selectedCategory]);
+
+ useEffect(() => {
+ let listString = '';
+ // Append non-truncated usernames together and no more than 21 characters total
+ // e.g. "@ivan.tagg"
+ // e.g. "@ivan.tagg @foobar . . ."
+ for (const tag of tags) {
+ const usernameStr = `@${tag.user.username} `;
+ if (listString.length + usernameStr.length > 21) {
+ listString = listString.concat('. . .');
+ break;
+ } else {
+ listString = listString.concat(usernameStr);
}
- setTaggedList(listString);
- };
- getTaggedUsersListString();
+ }
+ setTaggedUsersText(listString);
}, [tags]);
- const navigateToProfile = () => {
- //Since the logged In User is navigating to own profile, useXId is not required
- navigation.navigate('Profile', {
- screenType,
- userXId: undefined,
- });
- };
-
- const handleFailed = () => {
+ const handleFailed = (noCategory = false) => {
setLoading(false);
+ navigation.dangerouslyGetParent()?.setOptions({
+ tabBarVisible: true,
+ });
setTimeout(() => {
- Alert.alert(moment ? ERROR_SOMETHING_WENT_WRONG_REFRESH : ERROR_UPLOAD);
+ if (noCategory) {
+ Alert.alert(ERROR_NO_MOMENT_CATEGORY);
+ } else {
+ Alert.alert(moment ? ERROR_SOMETHING_WENT_WRONG_REFRESH : ERROR_UPLOAD);
+ }
}, 500);
};
const handleSuccess = () => {
setLoading(false);
- if (moment) {
- setLoading(false);
- navigation.goBack();
- } else {
- navigateToProfile();
+ navigation.dangerouslyGetParent()?.setOptions({
+ tabBarVisible: true,
+ });
+ if (!moment) {
+ // if posting, pop all screens until at camera screen (default upload screen)
+ // then switch to the profile tab
+ navigation.popToTop();
+ RootNavigation.navigate('ProfileTab');
setTimeout(() => {
- Alert.alert(SUCCESS_PIC_UPLOAD);
+ if (isMediaAVideo) {
+ Alert.alert(
+ 'Beautiful, the Moment was uploaded successfully! Check back in a bit and refresh to see it!',
+ );
+ } else {
+ Alert.alert(SUCCESS_PIC_UPLOAD);
+ }
}, 500);
+ } else {
+ // if editing, simply go back to profile screen
+ navigation.navigate('Profile', {
+ userXId: undefined,
+ screenType: route.params.screenType,
+ });
}
};
@@ -140,15 +167,15 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => {
const handleShare = async () => {
setLoading(true);
- if (moment || !title) {
- handleFailed();
+ if (moment || !momentCategory) {
+ handleFailed(true);
return;
}
let profileCompletionStage;
let momentId;
// separate upload logic for image/video
if (isMediaAVideo) {
- const presignedURLResponse = await handlePresignedURL(title);
+ const presignedURLResponse = await handlePresignedURL(momentCategory);
if (!presignedURLResponse) {
handleFailed();
return;
@@ -161,7 +188,12 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => {
handleFailed();
}
} else {
- const momentResponse = await postMoment(mediaUri, caption, title, userId);
+ const momentResponse = await postMoment(
+ mediaUri,
+ caption,
+ momentCategory,
+ userId,
+ );
if (!momentResponse) {
handleFailed();
return;
@@ -176,19 +208,22 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => {
return;
}
}
- dispatch(loadUserMoments(userId));
+ if (!isMediaAVideo) {
+ dispatch(loadUserMoments(userId));
+ }
if (profileCompletionStage) {
dispatch(updateProfileCompletionStage(profileCompletionStage));
}
handleSuccess();
};
- const handleDoneEditing = async () => {
+ const handleSubmitEditChanges = async () => {
setLoading(true);
- if (moment?.moment_id) {
+ if (moment?.moment_id && momentCategory) {
const success = await patchMoment(
moment.moment_id,
caption,
+ momentCategory,
formattedTags(),
);
if (success) {
@@ -200,17 +235,46 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => {
}
};
- const [currentTime, setCurrentTime] = useState(0);
- const [startTime, setStartTime] = useState(0);
- // todo: update with vid times
- const [endTime, setEndTime] = useState(60);
+ const SelectableItem: FC<{
+ text: 'Tag Friends' | 'Category';
+ imageUri: ImageSourcePropType;
+ onPress: () => void;
+ }> = ({text, imageUri, onPress}) => {
+ return (
+ <TouchableOpacity
+ onPress={onPress}
+ style={styles.selectableItemContainer}>
+ <View style={styles.row}>
+ {text === 'Category' && !momentCategory && (
+ <Text style={styles.asteriskText}>* </Text>
+ )}
+ <Image style={styles.tagIcon} source={imageUri} />
+ <Text style={styles.selectableItemTitle}>{text}</Text>
+ </View>
+ <View style={styles.row}>
+ {text === 'Tag Friends' && (
+ <Text style={styles.itemInfoText}>{taggedUsersText}</Text>
+ )}
+ {text === 'Category' && (
+ <Text style={styles.itemInfoText}>{momentCategory}</Text>
+ )}
+ <FrontArrow
+ width={normalize(13)}
+ height={normalize(13)}
+ color={'white'}
+ />
+ </View>
+ </TouchableOpacity>
+ );
+ };
return (
<SearchBackground>
- {loading ? <TaggLoadingIndicator fullscreen /> : <Fragment />}
+ {loading && <TaggLoadingIndicator fullscreen />}
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
+ keyboardVerticalOffset={isIPhoneX() ? 40 : 30}
style={styles.flex}>
<View style={styles.contentContainer}>
<View style={styles.buttonsContainer}>
@@ -219,104 +283,84 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => {
buttonStyle={styles.button}
onPress={() => navigation.goBack()}
/>
- <Button
- title={moment ? 'Done' : 'Share'}
- titleStyle={styles.shareButtonTitle}
- buttonStyle={styles.button}
- onPress={moment ? handleDoneEditing : handleShare}
- />
</View>
- <CaptionScreenHeader
- style={styles.header}
- {...{title: moment ? moment.moment_category : title ?? ''}}
- />
- {isMediaAVideo ? (
- <View style={styles.mediaContainer}>
- {/* <VideoPlayer
- //style={styles.media}
- ref={(ref) => (this.videoPlayerRef = ref)}
- startTime={startTime} // seconds
- endTime={endTime} // seconds
- play={true} // default false
- replay={true} // should player play video again if it's ended
- //rotate={true} // use this prop to rotate video if it captured in landscape mode iOS only
- source={mediaUri}
- playerWidth={360} // iOS only
- playerHeight={680} // iOS only
- //resizeMode={VideoPlayer.Constants.resizeMode.CONTAIN}
- onChange={({nativeEvent}) =>
- setCurrentTime(nativeEvent.currentTime)
- } // get Current time on every second
- />
- <Trimmer
- source={mediaUri}
- height={50}
- width={350}
- onTrackerMove={(e) => setCurrentTime(e.currentTime)} // iOS only
- currentTime={currentTime} // use this prop to set tracker position iOS only
- themeColor={'white'} // iOS only
- showTrackerHandle={true}
- thumbWidth={10} // iOS only
- trackerColor={'green'} // iOS only
- onChange={(e) => {
- setStartTime(e.startTime);
- setEndTime(e.endTime);
- }}
- /> */}
+ <CaptionScreenHeader style={styles.header} title={'Moments'} />
+ <View style={styles.captionContainer}>
+ {isMediaAVideo ? (
<Video
+ style={styles.media}
+ source={{uri: mediaUri}}
+ repeat={true}
+ />
+ ) : (
+ <Image
+ style={styles.media}
source={{uri: mediaUri}}
- volume={1}
- style={[
- styles.media,
- {
- height: SCREEN_WIDTH / aspectRatio,
- },
- ]}
- repeat={false}
resizeMode={'contain'}
- onLoad={(response) => {
- const {width, height} = response.naturalSize;
- setAspectRatio(width / height);
- }}
/>
- </View>
- ) : (
- <Image
- style={styles.media}
- source={{uri: mediaUri}}
- resizeMode={'contain'}
+ )}
+ <MentionInputControlled
+ style={styles.text}
+ containerStyle={styles.flex}
+ placeholder="Write something....."
+ placeholderTextColor="white"
+ value={caption}
+ onChange={setCaption}
+ partTypes={mentionPartTypes('white', 'caption', true)}
/>
+ </View>
+ {useMemo(
+ () => (
+ <SelectableItem
+ text={'Category'}
+ imageUri={require('../../assets/images/images.png')}
+ onPress={() =>
+ navigation.navigate('ChoosingCategoryScreen', {})
+ }
+ />
+ ),
+ [momentCategory],
)}
- <MentionInputControlled
- containerStyle={styles.text}
- placeholder="Write something....."
- placeholderTextColor="gray"
- value={caption}
- onChange={setCaption}
- partTypes={mentionPartTypes('blue', 'caption')}
- />
- <TouchableOpacity
- onPress={() =>
- navigation.navigate('TagFriendsScreen', {
- media: {
- uri: mediaUri,
- isVideo: isMediaAVideo,
- },
- selectedTags: tags,
- })
- }
- style={styles.tagFriendsContainer}>
- <Image
- source={require('../../assets/icons/tagging/tag-icon.png')}
- style={styles.tagIcon}
+ {useMemo(
+ () => (
+ <SelectableItem
+ text={'Tag Friends'}
+ imageUri={require('../../assets/icons/tagging/tag-icon.png')}
+ onPress={() =>
+ navigation.navigate('TagFriendsScreen', {
+ media: {
+ uri: mediaUri,
+ isVideo: isMediaAVideo,
+ },
+ selectedTags: tags,
+ })
+ }
+ />
+ ),
+ [taggedUsersText],
+ )}
+ {momentCategory ? (
+ <TaggSquareButton
+ onPress={moment ? handleSubmitEditChanges : handleShare}
+ title={moment ? 'Update' : 'Post'}
+ buttonStyle={'large'}
+ buttonColor={'blue'}
+ labelColor={'white'}
+ style={styles.postButton}
+ labelStyle={styles.postText}
+ />
+ ) : (
+ <TaggSquareButton
+ disabled={true}
+ onPress={moment ? handleSubmitEditChanges : handleShare}
+ title={moment ? 'Update' : 'Post'}
+ buttonStyle={'large'}
+ buttonColor={'blue'}
+ labelColor={'white'}
+ style={[styles.postButton, styles.greyBackground]}
+ labelStyle={styles.postText}
/>
- <Text style={styles.tagFriendsTitle}>Tag Friends</Text>
- <Text numberOfLines={1} style={styles.taggedListContainer}>
- {taggedList}
- {taggedList.length > 21 ? '. . .' : ''}
- </Text>
- <FrontArrow width={12} height={12} color={'white'} />
- </TouchableOpacity>
+ )}
</View>
</KeyboardAvoidingView>
</TouchableWithoutFeedback>
@@ -342,53 +386,90 @@ const styles = StyleSheet.create({
color: TAGG_LIGHT_BLUE_2,
},
header: {
- marginVertical: 20,
+ marginTop: 20,
+ marginBottom: 30,
},
- media: {
- position: 'relative',
- width: SCREEN_WIDTH,
- aspectRatio: 1,
- marginBottom: '3%',
- flex: 1,
+ captionContainer: {
+ flexDirection: 'row',
+ padding: normalize(15),
+ marginBottom: normalize(35),
+ borderColor: 'white',
+ borderTopWidth: 1,
+ borderBottomWidth: 1,
+ zIndex: 1,
},
- mediaContainer: {
- width: SCREEN_WIDTH,
- aspectRatio: 1,
+ media: {
+ height: normalize(150),
+ aspectRatio: 9 / 16,
},
text: {
- position: 'relative',
- backgroundColor: 'white',
- width: '100%',
- paddingHorizontal: '2%',
- paddingVertical: '1%',
- height: 60,
+ color: 'white',
+ fontSize: normalize(12),
+ lineHeight: 14,
+ fontWeight: '500',
+ height: normalize(150),
+ marginLeft: normalize(15),
},
flex: {
flex: 1,
},
- tagFriendsTitle: {
+ selectableItemTitle: {
color: 'white',
- fontSize: normalize(12),
+ fontSize: normalize(14),
lineHeight: normalize(16.71),
letterSpacing: normalize(0.3),
fontWeight: '600',
},
- tagFriendsContainer: {
+ selectableItemContainer: {
marginHorizontal: '5%',
- marginTop: '3%',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
+ marginBottom: normalize(42),
+ },
+ asteriskText: {
+ color: TAGG_LIGHT_BLUE_2,
+ fontWeight: 'bold',
+ fontSize: normalize(15),
+ height: 15,
+ alignSelf: 'center',
},
- taggedListContainer: {
+ itemInfoText: {
color: 'white',
width: 150,
fontSize: normalize(10),
lineHeight: normalize(11),
letterSpacing: normalize(0.3),
textAlign: 'right',
+
+ marginRight: 5,
+ },
+ tagIcon: {
+ width: normalize(20),
+ height: normalize(20),
+ marginRight: 15,
+ },
+ row: {
+ flexDirection: 'row',
+ },
+ greyBackground: {
+ backgroundColor: '#C4C4C4',
+ },
+ postButton: {
+ width: SCREEN_WIDTH * 0.8,
+ height: normalize(37),
+ justifyContent: 'center',
+ alignItems: 'center',
+ borderRadius: 6,
+ alignSelf: 'center',
+ },
+ postText: {
+ color: 'white',
+ fontWeight: 'bold',
+ fontSize: normalize(15),
+ lineHeight: 18,
+ letterSpacing: 2,
},
- tagIcon: {width: 20, height: 20},
});
export default CaptionScreen;
diff --git a/src/screens/profile/CategorySelection.tsx b/src/screens/profile/CategorySelection.tsx
index ea443fce..2f364e59 100644
--- a/src/screens/profile/CategorySelection.tsx
+++ b/src/screens/profile/CategorySelection.tsx
@@ -170,6 +170,7 @@ const CategorySelection: React.FC<CategorySelectionProps> = ({
onPress={() => {
navigation.push('CreateCustomCategory', {
existingCategories: momentCategories.concat(selectedCategories),
+ fromScreen: route.name,
});
}}>
<PlusIcon width={30} height={30} color="white" />
diff --git a/src/screens/profile/ChoosingCategoryScreen.tsx b/src/screens/profile/ChoosingCategoryScreen.tsx
new file mode 100644
index 00000000..8a7e3007
--- /dev/null
+++ b/src/screens/profile/ChoosingCategoryScreen.tsx
@@ -0,0 +1,192 @@
+import {useBottomTabBarHeight} from '@react-navigation/bottom-tabs';
+import {RouteProp, useNavigation} from '@react-navigation/native';
+import React, {FC, useEffect} from 'react';
+import {
+ Image,
+ ScrollView,
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+ View,
+} from 'react-native';
+import LinearGradient from 'react-native-linear-gradient';
+import {useDispatch, useSelector} from 'react-redux';
+import FrontArrow from '../../assets/icons/front-arrow.svg';
+import PlusIcon from '../../assets/icons/plus-icon.svg';
+import {SearchBackground, TaggSquareButton} from '../../components';
+import {TAGGS_GRADIENT, TAGG_DARK_PURPLEISH_BLUE} from '../../constants';
+import {MainStackParams} from '../../routes';
+import {updateMomentCategories} from '../../store/actions';
+import {RootState} from '../../store/rootReducer';
+import {
+ getMomentCategoryIconInfo,
+ HeaderHeight,
+ normalize,
+ SCREEN_HEIGHT,
+ SCREEN_WIDTH,
+ StatusBarHeight,
+} from '../../utils';
+
+type ChoosingCategoryScreenRouteProps = RouteProp<
+ MainStackParams,
+ 'ChoosingCategoryScreen'
+>;
+
+interface ChoosingCategoryScreenProps {
+ route: ChoosingCategoryScreenRouteProps;
+}
+
+const ChoosingCategoryScreen: React.FC<ChoosingCategoryScreenProps> = ({
+ route,
+}) => {
+ const {momentCategories} = useSelector(
+ (state: RootState) => state.momentCategories,
+ );
+ const dispatch = useDispatch();
+ const navigation = useNavigation();
+ const tabBarHeight = useBottomTabBarHeight();
+
+ useEffect(() => {
+ if (route.params.newCustomCategory) {
+ dispatch(
+ updateMomentCategories(
+ momentCategories.concat([route.params.newCustomCategory]),
+ false,
+ ),
+ );
+ }
+ }, [route.params.newCustomCategory]);
+
+ const ListItem: FC<{
+ title: string;
+ onPress: () => void;
+ }> = ({title, onPress}) => {
+ const icon = getMomentCategoryIconInfo(title).icon;
+ return (
+ <TouchableOpacity onPress={onPress} style={styles.itemContainer}>
+ <View style={styles.row}>
+ <LinearGradient
+ style={styles.gradientIcon}
+ colors={[TAGGS_GRADIENT.start, TAGGS_GRADIENT.end]}
+ useAngle={true}
+ angle={-45}>
+ <View style={styles.iconBackground}>
+ <Image style={styles.icon} source={icon} />
+ </View>
+ </LinearGradient>
+ <Text style={styles.itemTitle}>{title}</Text>
+ </View>
+ <View style={styles.row}>
+ <FrontArrow
+ width={normalize(13)}
+ height={normalize(13)}
+ color={'white'}
+ />
+ </View>
+ </TouchableOpacity>
+ );
+ };
+
+ return (
+ <SearchBackground>
+ <View style={{marginTop: StatusBarHeight + HeaderHeight}}>
+ <ScrollView
+ style={{height: SCREEN_HEIGHT * 0.9}}
+ contentContainerStyle={{paddingBottom: tabBarHeight}}>
+ {momentCategories.map((title) => (
+ <ListItem
+ key={title}
+ title={title}
+ onPress={() =>
+ navigation.navigate('CaptionScreen', {
+ selectedCategory: title,
+ })
+ }
+ />
+ ))}
+ <TaggSquareButton
+ onPress={() =>
+ navigation.navigate('CreateCustomCategory', {
+ existingCategories: momentCategories,
+ fromScreen: route.name,
+ })
+ }
+ title={'Create a new category'}
+ buttonStyle={'large'}
+ buttonColor={'blue'}
+ labelColor={'white'}
+ style={styles.button}
+ labelStyle={styles.buttonText}
+ icon={<PlusIcon style={styles.plusIcon} />}
+ />
+ </ScrollView>
+ </View>
+ </SearchBackground>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ marginTop: StatusBarHeight,
+ },
+ itemContainer: {
+ marginHorizontal: '5%',
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginVertical: normalize(20),
+ borderRadius: 4,
+ },
+ gradientIcon: {
+ width: normalize(40),
+ height: normalize(40),
+ justifyContent: 'center',
+ alignItems: 'center',
+ borderRadius: 4,
+ },
+ iconBackground: {
+ height: '85%',
+ width: '85%',
+ justifyContent: 'center',
+ alignItems: 'center',
+ backgroundColor: TAGG_DARK_PURPLEISH_BLUE,
+ },
+ icon: {
+ height: normalize(25),
+ width: normalize(25),
+ },
+ itemTitle: {
+ color: 'white',
+ fontSize: normalize(14),
+ lineHeight: normalize(16.71),
+ letterSpacing: normalize(0.3),
+ fontWeight: '600',
+ alignSelf: 'center',
+ marginLeft: normalize(25),
+ },
+ row: {
+ flexDirection: 'row',
+ },
+ button: {
+ width: SCREEN_WIDTH * 0.9,
+ height: normalize(67),
+ justifyContent: 'center',
+ alignItems: 'center',
+ borderRadius: 8,
+ alignSelf: 'center',
+ marginTop: 40,
+ },
+ buttonText: {
+ color: 'white',
+ fontSize: normalize(15),
+ lineHeight: 18,
+ },
+ plusIcon: {
+ color: 'white',
+ marginRight: normalize(25),
+ width: 30,
+ height: 30,
+ },
+});
+
+export default ChoosingCategoryScreen;
diff --git a/src/screens/profile/CreateCustomCategory.tsx b/src/screens/profile/CreateCustomCategory.tsx
index c4b17b1e..91083c7c 100644
--- a/src/screens/profile/CreateCustomCategory.tsx
+++ b/src/screens/profile/CreateCustomCategory.tsx
@@ -37,14 +37,14 @@ const CreateCustomCategory: React.FC<CreateCustomCategoryProps> = ({
/**
* Same component to be used for category selection while onboarding and while on profile
*/
- const {existingCategories} = route.params;
+ const {existingCategories, fromScreen} = route.params;
const [newCategory, setNewCategory] = useState('');
const handleButtonPress = () => {
if (existingCategories.includes(newCategory)) {
Alert.alert('Looks like you already have that one created!');
} else {
- navigation.navigate('CategorySelection', {
+ navigation.navigate(fromScreen, {
newCustomCategory: newCategory,
});
}
diff --git a/src/screens/profile/IndividualMoment.tsx b/src/screens/profile/IndividualMoment.tsx
index 7d231312..1b0c7c2b 100644
--- a/src/screens/profile/IndividualMoment.tsx
+++ b/src/screens/profile/IndividualMoment.tsx
@@ -92,6 +92,8 @@ const IndividualMoment: React.FC<IndividualMomentProps> = ({route}) => {
screenType={screenType}
/>
)}
+ keyboardShouldPersistTaps={'handled'}
+ scrollEnabled={!keyboardVisible}
keyExtractor={(item, _) => item.moment_id}
showsVerticalScrollIndicator={false}
initialScrollIndex={initialIndex}
diff --git a/src/screens/profile/MomentCommentsScreen.tsx b/src/screens/profile/MomentCommentsScreen.tsx
index 402e5f44..7dfe8ae9 100644
--- a/src/screens/profile/MomentCommentsScreen.tsx
+++ b/src/screens/profile/MomentCommentsScreen.tsx
@@ -48,8 +48,9 @@ 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/screens/profile/index.ts b/src/screens/profile/index.ts
index ea0505a2..101101b8 100644
--- a/src/screens/profile/index.ts
+++ b/src/screens/profile/index.ts
@@ -13,3 +13,4 @@ export {default as AccountType} from './AccountType';
export {default as CategorySelection} from './CategorySelection';
export {default as CreateCustomCategory} from './CreateCustomCategory';
export {default as CommentReactionScreen} from './CommentReactionScreen';
+export {default as ChoosingCategoryScreen} from './ChoosingCategoryScreen';
diff --git a/src/services/MomentService.ts b/src/services/MomentService.ts
index 0c93876a..50cedef9 100644
--- a/src/services/MomentService.ts
+++ b/src/services/MomentService.ts
@@ -59,6 +59,7 @@ export const postMoment = async (
export const patchMoment = async (
momentId: string,
caption: string,
+ category: string,
tags: {
x: number;
y: number;
@@ -69,7 +70,8 @@ export const patchMoment = async (
try {
const request = new FormData();
request.append('moment_id', momentId);
- request.append('captions', JSON.stringify({[momentId]: caption}));
+ request.append('caption', caption);
+ request.append('category', category);
request.append('tags', JSON.stringify(tags));
const token = await AsyncStorage.getItem('token');
let response = await fetch(MOMENTS_ENDPOINT, {
diff --git a/src/utils/comments.tsx b/src/utils/comments.tsx
index 28879622..504631f5 100644
--- a/src/utils/comments.tsx
+++ b/src/utils/comments.tsx
@@ -82,12 +82,17 @@ export const renderTextWithMentions: React.FC<RenderProps> = ({
export const mentionPartTypes: (
theme: 'blue' | 'white',
component: 'caption' | 'comment',
-) => PartType[] = (theme, component) => {
+ isShowBelowStyle?: boolean,
+) => PartType[] = (theme, component, isShowBelowStyle = false) => {
return [
{
trigger: '@',
renderSuggestions: (props) => (
- <TaggTypeahead component={component} {...props} />
+ <TaggTypeahead
+ component={component}
+ isShowBelowStyle={isShowBelowStyle}
+ {...props}
+ />
),
allowedSpacesCount: 0,
isInsertSpaceAfterMention: true,
diff --git a/src/utils/moments.ts b/src/utils/moments.ts
index 9e8cc332..4aadb4d4 100644
--- a/src/utils/moments.ts
+++ b/src/utils/moments.ts
@@ -1,4 +1,6 @@
import moment from 'moment';
+import {ImageSourcePropType} from 'react-native';
+import {MOMENT_CATEGORY_BG_COLORS} from '../constants';
/**
* Formats elapsed time from a given time.
@@ -71,3 +73,85 @@ export const getTimeInShorthand = (date_time: string) => {
}
return time;
};
+
+export const getMomentCategoryIconInfo = (category: string) => {
+ let icon: ImageSourcePropType, bgColor: string;
+ switch (category) {
+ case 'Friends':
+ icon = require('../assets/moment-categories/friends-icon.png');
+ bgColor = MOMENT_CATEGORY_BG_COLORS[0];
+ break;
+ case 'Adventure':
+ icon = require('../assets/moment-categories/adventure-icon.png');
+ bgColor = MOMENT_CATEGORY_BG_COLORS[1];
+ break;
+ case 'Photo Dump':
+ icon = require('../assets/moment-categories/photo-dump-icon.png');
+ bgColor = MOMENT_CATEGORY_BG_COLORS[2];
+ break;
+ case 'Food':
+ icon = require('../assets/moment-categories/food-icon.png');
+ bgColor = MOMENT_CATEGORY_BG_COLORS[3];
+ break;
+ case 'Music':
+ icon = require('../assets/moment-categories/music-icon.png');
+ bgColor = MOMENT_CATEGORY_BG_COLORS[4];
+ break;
+ case 'Art':
+ icon = require('../assets/moment-categories/art-icon.png');
+ bgColor = MOMENT_CATEGORY_BG_COLORS[5];
+ break;
+ case 'Sports':
+ icon = require('../assets/moment-categories/sports-icon.png');
+ bgColor = MOMENT_CATEGORY_BG_COLORS[6];
+ break;
+ case 'Fashion':
+ icon = require('../assets/moment-categories/fashion-icon.png');
+ bgColor = MOMENT_CATEGORY_BG_COLORS[7];
+ break;
+ case 'Travel':
+ icon = require('../assets/moment-categories/travel-icon.png');
+ bgColor = MOMENT_CATEGORY_BG_COLORS[8];
+ break;
+ case 'Pets':
+ icon = require('../assets/moment-categories/pets-icon.png');
+ bgColor = MOMENT_CATEGORY_BG_COLORS[9];
+ break;
+ case 'Fitness':
+ icon = require('../assets/moment-categories/fitness-icon.png');
+ bgColor = MOMENT_CATEGORY_BG_COLORS[10];
+ break;
+ case 'DIY':
+ icon = require('../assets/moment-categories/diy-icon.png');
+ bgColor = MOMENT_CATEGORY_BG_COLORS[11];
+ break;
+ case 'Nature':
+ icon = require('../assets/moment-categories/nature-icon.png');
+ bgColor = MOMENT_CATEGORY_BG_COLORS[12];
+ break;
+ case 'Early Life':
+ icon = require('../assets/moment-categories/early-life-icon.png');
+ bgColor = MOMENT_CATEGORY_BG_COLORS[13];
+ break;
+ case 'Beauty':
+ icon = require('../assets/moment-categories/beauty-icon.png');
+ bgColor = MOMENT_CATEGORY_BG_COLORS[14];
+ break;
+ default:
+ // All custom categories
+ icon = require('../assets/moment-categories/custom-icon.png');
+ // A quick deterministic "random" color picker by summing up ascii char codees
+ const charCodeSum = category
+ .split('')
+ .reduce((acc: number, x: string) => acc + x.charCodeAt(0), 0);
+ bgColor =
+ MOMENT_CATEGORY_BG_COLORS[
+ charCodeSum % MOMENT_CATEGORY_BG_COLORS.length
+ ];
+ break;
+ }
+ return {
+ icon,
+ bgColor,
+ };
+};