aboutsummaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
authorAshm Walia <40498934+ashmgarv@users.noreply.github.com>2020-12-22 08:50:27 -0800
committerGitHub <noreply@github.com>2020-12-22 11:50:27 -0500
commita954d6b6b88485dddc0ccfda634ffd102cb34ccd (patch)
tree560f152dd92ccb482a2bbf6b094060525373322c /src/components
parent49ed044f5103cf6288fcf5b3ff6d3d720795860c (diff)
[TMA 446] Create category (#144)
* Added welcome page * Working code * Small fix * Some more cleanup * Fixes * Cleanup * Fix again * Use gradient for white bg as well * Fixed type
Diffstat (limited to 'src/components')
-rw-r--r--src/components/common/ComingSoon.tsx5
-rw-r--r--src/components/common/TaggPopup.tsx133
-rw-r--r--src/components/common/index.ts1
-rw-r--r--src/components/moments/Moment.tsx32
-rw-r--r--src/components/onboarding/Background.tsx12
-rw-r--r--src/components/onboarding/MomentCategory.tsx175
-rw-r--r--src/components/onboarding/index.ts1
-rw-r--r--src/components/profile/Content.tsx96
8 files changed, 435 insertions, 20 deletions
diff --git a/src/components/common/ComingSoon.tsx b/src/components/common/ComingSoon.tsx
index 16b65b58..d7654a20 100644
--- a/src/components/common/ComingSoon.tsx
+++ b/src/components/common/ComingSoon.tsx
@@ -1,11 +1,14 @@
import * as React from 'react';
import {StyleSheet, View, Text, Image} from 'react-native';
+import {BackgroundGradientType} from '../../types';
import {SCREEN_WIDTH} from '../../utils';
import {Background} from '../onboarding';
const ComingSoon: React.FC = () => {
return (
- <Background style={styles.container}>
+ <Background
+ style={styles.container}
+ gradientType={BackgroundGradientType.Light}>
<View style={styles.textContainer}>
<Text style={styles.header}>Coming Soon</Text>
<Text style={styles.subtext}>
diff --git a/src/components/common/TaggPopup.tsx b/src/components/common/TaggPopup.tsx
new file mode 100644
index 00000000..db24adb8
--- /dev/null
+++ b/src/components/common/TaggPopup.tsx
@@ -0,0 +1,133 @@
+import {RouteProp} from '@react-navigation/native';
+import {StackNavigationProp} from '@react-navigation/stack';
+import * as React from 'react';
+import {Platform, Text, StyleSheet, TouchableOpacity} from 'react-native';
+import {Image, View} from 'react-native-animatable';
+import {ArrowButton} from '..';
+import {OnboardingStackParams} from '../../routes';
+import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
+import CloseIcon from '../../assets/ionicons/close-outline.svg';
+
+type TaggPopupRouteProps = RouteProp<OnboardingStackParams, 'TaggPopup'>;
+type TaggPopupNavigationProps = StackNavigationProp<
+ OnboardingStackParams,
+ 'TaggPopup'
+>;
+
+interface TaggPopupProps {
+ route: TaggPopupRouteProps;
+ navigation: TaggPopupNavigationProps;
+}
+
+const TaggPopup: React.FC<TaggPopupProps> = ({route, navigation}) => {
+ /**
+ * Custom popup / Tutorial screen for Tagg
+ * Just like a Singly Linked List, we have a next node
+ * if (next !== undefined)
+ * Display the next button and navigate to next popup node on click
+ * else
+ * Display close button, navigate back on close
+ */
+ const {messageHeader, messageBody, next} = route.params.popupProps;
+
+ return (
+ <View style={styles.container}>
+ <View style={styles.popup}>
+ <Image
+ style={styles.icon}
+ source={require('../../assets/icons/plus-logo.png')}
+ />
+ <View style={styles.textContainer}>
+ <Text style={styles.header}>{messageHeader}</Text>
+ <Text style={styles.subtext}>{messageBody}</Text>
+ </View>
+ {!next && (
+ <TouchableOpacity
+ style={styles.closeButton}
+ onPress={() => {
+ navigation.goBack();
+ }}>
+ <CloseIcon height={'50%'} width={'50%'} color={'white'} />
+ </TouchableOpacity>
+ )}
+ </View>
+ {next && (
+ <View style={styles.footer}>
+ <ArrowButton
+ direction="forward"
+ onPress={() => {
+ navigation.navigate('TaggPopup', {popupProps: next});
+ }}
+ />
+ </View>
+ )}
+ </View>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ flexDirection: 'column',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ whiteColor: {
+ color: 'white',
+ },
+ closeButton: {
+ position: 'relative',
+ height: '50%',
+ aspectRatio: 1,
+ left: '20%',
+ },
+ textContainer: {
+ flex: 1,
+ flexDirection: 'column',
+ },
+ icon: {
+ width: 40,
+ height: 40,
+ marginVertical: '1%',
+ },
+ header: {
+ color: '#fff',
+ fontSize: 16,
+ fontWeight: '600',
+ textAlign: 'justify',
+ marginBottom: '2%',
+ marginHorizontal: '2%',
+ },
+ subtext: {
+ color: '#fff',
+ fontSize: 12,
+ fontWeight: '600',
+ textAlign: 'justify',
+ marginBottom: '15%',
+ marginHorizontal: '2%',
+ },
+ popup: {
+ width: SCREEN_WIDTH * 0.8,
+ height: SCREEN_WIDTH * 0.2,
+ backgroundColor: 'black',
+ borderRadius: 8,
+ flexDirection: 'row',
+ alignSelf: 'auto',
+ flexWrap: 'wrap',
+ position: 'absolute',
+ bottom: SCREEN_HEIGHT * 0.7,
+ },
+ footer: {
+ marginLeft: '50%',
+ flexDirection: 'column-reverse',
+ ...Platform.select({
+ ios: {
+ bottom: '20%',
+ },
+ android: {
+ bottom: '10%',
+ },
+ }),
+ },
+});
+export default TaggPopup;
diff --git a/src/components/common/index.ts b/src/components/common/index.ts
index 661d2f52..d5d36297 100644
--- a/src/components/common/index.ts
+++ b/src/components/common/index.ts
@@ -17,3 +17,4 @@ export {default as TaggDatePicker} from './TaggDatePicker';
export {default as BottomDrawer} from './BottomDrawer';
export {default as TaggLoadingTndicator} from './TaggLoadingIndicator';
export {default as GenericMoreInfoDrawer} from './GenericMoreInfoDrawer';
+export {default as TaggPopUp} from './TaggPopup';
diff --git a/src/components/moments/Moment.tsx b/src/components/moments/Moment.tsx
index 940b519c..fb6186c8 100644
--- a/src/components/moments/Moment.tsx
+++ b/src/components/moments/Moment.tsx
@@ -5,18 +5,21 @@ import {Text} from 'react-native-animatable';
import {ScrollView, TouchableOpacity} from 'react-native-gesture-handler';
import LinearGradient from 'react-native-linear-gradient';
import PlusIcon from '../../assets/icons/plus_icon-01.svg';
+import DeleteIcon from '../../assets/icons/delete-logo.svg';
import BigPlusIcon from '../../assets/icons/plus_icon-02.svg';
import {TAGG_TEXT_LIGHT_BLUE} from '../../constants';
import {SCREEN_WIDTH} from '../../utils';
import ImagePicker from 'react-native-image-crop-picker';
import MomentTile from './MomentTile';
-import {MomentType, ScreenType} from 'src/types';
+import {MomentCategoryType, MomentType, ScreenType} from 'src/types';
interface MomentProps {
- title: string;
+ title: MomentCategoryType;
images: MomentType[] | undefined;
userXId: string | undefined;
screenType: ScreenType;
+ handleMomentCategoryDelete: (_: MomentCategoryType) => void;
+ shouldAllowDeletion: boolean;
}
const Moment: React.FC<MomentProps> = ({
@@ -24,6 +27,8 @@ const Moment: React.FC<MomentProps> = ({
images,
userXId,
screenType,
+ handleMomentCategoryDelete,
+ shouldAllowDeletion,
}) => {
const navigation = useNavigation();
@@ -53,11 +58,21 @@ const Moment: React.FC<MomentProps> = ({
<View style={styles.header}>
<Text style={styles.titleText}>{title}</Text>
{!userXId ? (
- <PlusIcon
- width={21}
- height={21}
- onPress={() => navigateToImagePicker()}
- />
+ <>
+ <PlusIcon
+ width={21}
+ height={21}
+ onPress={() => navigateToImagePicker()}
+ style={{marginRight: 10}}
+ />
+ {shouldAllowDeletion && (
+ <DeleteIcon
+ onPress={() => handleMomentCategoryDelete(title)}
+ width={19}
+ height={19}
+ />
+ )}
+ </>
) : (
<React.Fragment />
)}
@@ -113,6 +128,9 @@ const styles = StyleSheet.create({
fontSize: 16,
fontWeight: 'bold',
color: TAGG_TEXT_LIGHT_BLUE,
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'flex-end',
},
scrollContainer: {
height: SCREEN_WIDTH / 3.25,
diff --git a/src/components/onboarding/Background.tsx b/src/components/onboarding/Background.tsx
index 054eeff6..fb08e945 100644
--- a/src/components/onboarding/Background.tsx
+++ b/src/components/onboarding/Background.tsx
@@ -8,23 +8,27 @@ import {
SafeAreaView,
} from 'react-native';
import {CenteredView} from '../common';
+import {BackgroundGradientType} from '../../types';
+import {BACKGROUND_GRADIENT_MAP} from '../../constants';
interface BackgroundProps extends ViewProps {
centered?: boolean;
+ gradientType: BackgroundGradientType;
}
const Background: React.FC<BackgroundProps> = (props) => {
+ const {centered, gradientType, children} = props;
return (
<LinearGradient
- colors={['#9F00FF', '#27EAE9']}
+ colors={BACKGROUND_GRADIENT_MAP[gradientType]}
useAngle={true}
angle={154.72}
angleCenter={{x: 0.5, y: 0.5}}
style={styles.container}>
<TouchableWithoutFeedback accessible={false} onPress={Keyboard.dismiss}>
- {props.centered ? (
- <CenteredView {...props}>{props.children}</CenteredView>
+ {centered ? (
+ <CenteredView {...props}>{children}</CenteredView>
) : (
- <SafeAreaView {...props}>{props.children}</SafeAreaView>
+ <SafeAreaView {...props}>{children}</SafeAreaView>
)}
</TouchableWithoutFeedback>
</LinearGradient>
diff --git a/src/components/onboarding/MomentCategory.tsx b/src/components/onboarding/MomentCategory.tsx
new file mode 100644
index 00000000..25e8995a
--- /dev/null
+++ b/src/components/onboarding/MomentCategory.tsx
@@ -0,0 +1,175 @@
+import * as React from 'react';
+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 {MomentCategoryType} from '../../types';
+import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
+
+type MomentCategoryProps = {
+ categoryType: MomentCategoryType;
+ onSelect: (
+ category: MomentCategoryType,
+ isSelected: boolean,
+ isAdded: boolean,
+ ) => void;
+ isSelected: boolean;
+ isAdded: boolean;
+};
+
+const MomentCategory: React.FC<MomentCategoryProps> = ({
+ categoryType,
+ isSelected,
+ 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 = '#5E4AE4';
+ break;
+ case 'Adventure':
+ icon = require('../../assets/moment-categories/adventure-icon.png');
+ bgColor = '#5044A6';
+ break;
+ case 'Photo Dump':
+ icon = require('../../assets/moment-categories/photo-dump-icon.png');
+ bgColor = '#4755A1';
+ break;
+ case 'Food':
+ icon = require('../../assets/moment-categories/food-icon.png');
+ bgColor = '#444BA8';
+ break;
+ case 'Music':
+ icon = require('../../assets/moment-categories/music-icon.png');
+ bgColor = '#374898';
+ break;
+ case 'Art':
+ icon = require('../../assets/moment-categories/art-icon.png');
+ bgColor = '#3F5C97';
+ break;
+ case 'Sports':
+ icon = require('../../assets/moment-categories/sports-icon.png');
+ bgColor = '#3A649F';
+ break;
+ case 'Fashion':
+ icon = require('../../assets/moment-categories/fashion-icon.png');
+ bgColor = '#386A95';
+ break;
+ case 'Travel':
+ icon = require('../../assets/moment-categories/travel-icon.png');
+ bgColor = '#366D84';
+ break;
+ case 'Pets':
+ icon = require('../../assets/moment-categories/pets-icon.png');
+ bgColor = '#335E76';
+ break;
+ case 'Nightlife':
+ icon = require('../../assets/moment-categories/nightlife-icon.png');
+ bgColor = '#2E5471';
+ break;
+ case 'DIY':
+ icon = require('../../assets/moment-categories/diy-icon.png');
+ bgColor = '#274765';
+ break;
+ case 'Nature':
+ icon = require('../../assets/moment-categories/nature-icon.png');
+ bgColor = '#225363';
+ break;
+ case 'Early Life':
+ icon = require('../../assets/moment-categories/early-life-icon.png');
+ bgColor = '#365F6A';
+ break;
+ case 'Beauty':
+ icon = require('../../assets/moment-categories/beauty-icon.png');
+ bgColor = '#4E7175';
+ break;
+ }
+
+ /**
+ * The Linear Gradient helps us add a gradient border when the category is already added /selected by user
+ * if(isAdded)
+ * gradient background
+ * if(isSelected)
+ * white background
+ * else
+ * transparent background
+ */
+ return (
+ <LinearGradient
+ colors={
+ isAdded
+ ? BACKGROUND_GRADIENT_MAP[0]
+ : isSelected
+ ? ['white', 'white']
+ : ['transparent', 'transparent']
+ }
+ start={{x: 0, y: 0}}
+ end={{x: 1, y: 0}}
+ style={[styles.container, styles.gradient]}>
+ <TouchableOpacity
+ activeOpacity={0.5}
+ onPress={() => onSelect(categoryType, !isSelected, isAdded)}
+ style={[
+ styles.container,
+ styles.touchable,
+ {backgroundColor: bgColor},
+ ]}>
+ <Image source={icon} style={styles.icon} />
+ <Text style={styles.label}>{categoryType}</Text>
+ {isAdded && (
+ <Image
+ source={require('../../assets/images/link-tick.png')}
+ style={styles.tick}
+ />
+ )}
+ </TouchableOpacity>
+ </LinearGradient>
+ );
+};
+
+const styles = StyleSheet.create({
+ gradient: {
+ width: SCREEN_WIDTH / 3.7,
+ height: SCREEN_HEIGHT / 5.8,
+ marginHorizontal: '2%',
+ marginVertical: '2%',
+ },
+ touchable: {
+ width: SCREEN_WIDTH / 4,
+ height: SCREEN_HEIGHT / 6.2,
+ marginHorizontal: '2%',
+ marginVertical: '4%',
+ },
+ container: {
+ borderRadius: 8,
+ shadowRadius: 2,
+ shadowOffset: {width: 0, height: 2},
+ shadowOpacity: 0.4,
+ flexDirection: 'column',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ icon: {
+ width: 40,
+ height: 40,
+ marginVertical: '8%',
+ },
+ label: {
+ fontWeight: '500',
+ color: 'white',
+ },
+ tick: {
+ marginTop: '3%',
+ width: 15,
+ height: 15,
+ },
+});
+
+export default MomentCategory;
diff --git a/src/components/onboarding/index.ts b/src/components/onboarding/index.ts
index fde4e0af..b790933f 100644
--- a/src/components/onboarding/index.ts
+++ b/src/components/onboarding/index.ts
@@ -9,3 +9,4 @@ export {default as BirthDatePicker} from './BirthDatePicker';
export {default as TaggDropDown} from './TaggDropDown';
export {default as SocialMediaLinker} from './SocialMediaLinker';
export {default as LinkSocialMedia} from './LinkSocialMedia';
+export {default as MomentCategory} from './MomentCategory';
diff --git a/src/components/profile/Content.tsx b/src/components/profile/Content.tsx
index f2e0db0a..7064f775 100644
--- a/src/components/profile/Content.tsx
+++ b/src/components/profile/Content.tsx
@@ -1,21 +1,29 @@
import React, {useCallback, useEffect, useState} from 'react';
import {
+ Alert,
LayoutChangeEvent,
NativeScrollEvent,
NativeSyntheticEvent,
RefreshControl,
StyleSheet,
+ Text,
View,
} from 'react-native';
import Animated from 'react-native-reanimated';
import {
+ CategorySelectionScreenType,
+ MomentCategoryType,
MomentType,
ProfilePreviewType,
ProfileType,
ScreenType,
UserType,
} from '../../types';
-import {COVER_HEIGHT, defaultMoments} from '../../constants';
+import {
+ COVER_HEIGHT,
+ MOMENT_CATEGORIES,
+ TAGG_TEXT_LIGHT_BLUE,
+} from '../../constants';
import {fetchUserX, SCREEN_HEIGHT, userLogin} from '../../utils';
import TaggsBar from '../taggs/TaggsBar';
import {Moment} from '../moments';
@@ -29,15 +37,19 @@ import {
blockUnblockUser,
loadFollowData,
updateUserXFollowersAndFollowing,
+ updateMomentCategories,
} from '../../store/actions';
import {
NO_USER,
NO_PROFILE,
EMPTY_PROFILE_PREVIEW_LIST,
EMPTY_MOMENTS_LIST,
+ MOMENT_CATEGORIES_MAP,
} from '../../store/initialStates';
import {Cover} from '.';
-import {Background} from '../onboarding';
+import {TouchableOpacity} from 'react-native-gesture-handler';
+import {useNavigation} from '@react-navigation/native';
+import {deleteMomentCategories} from '../../services';
interface ContentProps {
y: Animated.Value<number>;
@@ -60,6 +72,10 @@ const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => {
? useSelector((state: RootState) => state.userX[screenType][userXId])
: useSelector((state: RootState) => state.moments);
+ const {momentCategories = MOMENT_CATEGORIES_MAP} = userXId
+ ? useSelector((state: RootState) => state.userX[screenType][userXId])
+ : useSelector((state: RootState) => state.momentCategories);
+
const {blockedUsers = EMPTY_PROFILE_PREVIEW_LIST} = useSelector(
(state: RootState) => state.blocked,
);
@@ -68,6 +84,8 @@ const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => {
);
const state = useStore().getState();
+ const navigation = useNavigation();
+
/**
* States
*/
@@ -80,6 +98,13 @@ const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => {
const [shouldBounce, setShouldBounce] = useState<boolean>(true);
const [refreshing, setRefreshing] = useState<boolean>(false);
+ /**
+ * Filter list of categories already selected by user
+ */
+ const userMomentCategories = MOMENT_CATEGORIES.filter(
+ (category) => momentCategories[category] === true,
+ );
+
const onRefresh = useCallback(() => {
const refrestState = async () => {
if (!userXId) {
@@ -194,6 +219,33 @@ const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => {
await dispatch(updateUserXFollowersAndFollowing(user.userId, state));
};
+ /**
+ * Handle deletion of a category
+ * Confirm with user before deleting the category
+ * @param category category to be deleted
+ */
+ const handleCategoryDeletion = (category: MomentCategoryType) => {
+ Alert.alert(
+ 'Category Deletion',
+ `Are you sure that you want to delete the category ${category} ?`,
+ [
+ {
+ text: 'Cancel',
+ style: 'cancel',
+ },
+ {
+ text: 'Yes',
+ onPress: () => {
+ dispatch(
+ updateMomentCategories([category], false, loggedInUser.userId),
+ );
+ },
+ },
+ ],
+ {cancelable: true},
+ );
+ };
+
const handleScroll = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
/**
* Set the new y position
@@ -239,32 +291,60 @@ const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => {
/>
<TaggsBar {...{y, profileBodyHeight, userXId, screenType}} />
<View style={styles.momentsContainer}>
- {defaultMoments.map((title, index) => (
+ {userMomentCategories.map((title, index) => (
<Moment
key={index}
title={title}
images={imagesMap.get(title)}
userXId={userXId}
screenType={screenType}
+ handleMomentCategoryDelete={handleCategoryDeletion}
+ shouldAllowDeletion={userMomentCategories.length > 2}
/>
))}
+ {!userXId && userMomentCategories.length < 6 && (
+ <TouchableOpacity
+ onPress={() =>
+ navigation.push('CategorySelection', {
+ categories: momentCategories,
+ screenType: CategorySelectionScreenType.Profile,
+ user: loggedInUser,
+ })
+ }
+ style={styles.createCategoryButton}>
+ <Text style={styles.createCategoryButtonLabel}>
+ Create a new category
+ </Text>
+ </TouchableOpacity>
+ )}
</View>
</Animated.ScrollView>
);
};
const styles = StyleSheet.create({
- refreshControlContainer: {
- flex: 1,
- justifyContent: 'center',
- alignItems: 'center',
- },
container: {
flex: 1,
},
momentsContainer: {
backgroundColor: '#f2f2f2',
paddingBottom: SCREEN_HEIGHT / 10,
+ flex: 1,
+ flexDirection: 'column',
+ },
+ createCategoryButton: {
+ backgroundColor: TAGG_TEXT_LIGHT_BLUE,
+ justifyContent: 'center',
+ alignItems: 'center',
+ width: '70%',
+ height: 30,
+ marginTop: '15%',
+ alignSelf: 'center',
+ },
+ createCategoryButtonLabel: {
+ fontSize: 16,
+ fontWeight: '500',
+ color: 'white',
},
});