aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Chen <ivan@thetaggid.com>2020-12-07 19:19:50 -0500
committerGitHub <noreply@github.com>2020-12-07 19:19:50 -0500
commit84616a6143fea58899eafa915a73592eaad25361 (patch)
treef99a662a1661348faa8d9836fb5a97ff687addf0
parenta6dd130d5b89650e1ff134595c071f7f9c6be114 (diff)
[TMA-409] Delete Moments (#124)
* edit profile to use HeaderHeight instead of percentage * delete moment done with TODOs for backend * actually hitting the endpoint * ops * moved isProfileView check to drawer component * misunderstood suggestion, now is better * an extra layer of indirection * fixed some bugs and code improvement
-rw-r--r--src/components/common/GenericMoreInfoDrawer.tsx93
-rw-r--r--src/components/common/index.ts1
-rw-r--r--src/components/profile/MomentMoreInfoDrawer.tsx58
-rw-r--r--src/components/profile/MoreInfoDrawer.tsx88
-rw-r--r--src/components/profile/ProfileHeader.tsx34
-rw-r--r--src/components/profile/ProfileMoreInfoDrawer.tsx90
-rw-r--r--src/components/profile/index.ts3
-rw-r--r--src/screens/profile/EditProfile.tsx8
-rw-r--r--src/screens/profile/IndividualMoment.tsx75
-rw-r--r--src/services/MomentServices.ts22
10 files changed, 317 insertions, 155 deletions
diff --git a/src/components/common/GenericMoreInfoDrawer.tsx b/src/components/common/GenericMoreInfoDrawer.tsx
new file mode 100644
index 00000000..5c58f903
--- /dev/null
+++ b/src/components/common/GenericMoreInfoDrawer.tsx
@@ -0,0 +1,93 @@
+import React from 'react';
+import {
+ GestureResponderEvent,
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+ View,
+ ViewProps,
+ ViewStyle,
+} from 'react-native';
+import {useSafeAreaInsets} from 'react-native-safe-area-context';
+import {BottomDrawer} from '.';
+import {TAGG_TEXT_LIGHT_BLUE} from '../../constants';
+import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
+
+// conforms the JSX onPress attribute type
+type OnPressHandler = (event: GestureResponderEvent) => void;
+
+interface GenericMoreInfoDrawerProps extends ViewProps {
+ isOpen: boolean;
+ setIsOpen: (visible: boolean) => void;
+ showIcons: boolean;
+ // An array of title, onPressHandler, and icon component
+ buttons: [string, OnPressHandler, JSX.Element?][];
+}
+
+const GenericMoreInfoDrawer: React.FC<GenericMoreInfoDrawerProps> = (props) => {
+ const {buttons, showIcons} = props;
+ // each button is 80px high, cancel button is always there
+ const initialSnapPosition =
+ (buttons.length + 1) * 80 + useSafeAreaInsets().bottom;
+ let panelButtonStyle: ViewStyle[] = [
+ {
+ height: 80,
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ showIcons ? {} : {justifyContent: 'center'},
+ ];
+ return (
+ <BottomDrawer
+ {...props}
+ showHeader={false}
+ initialSnapPosition={initialSnapPosition}>
+ <View style={styles.panel}>
+ {buttons.map(([title, action, icon], index) => (
+ <View key={index}>
+ <TouchableOpacity style={panelButtonStyle} onPress={action}>
+ {showIcons && <View style={styles.icon}>{icon}</View>}
+ <Text style={styles.panelButtonTitle}>{title}</Text>
+ </TouchableOpacity>
+ <View style={styles.divider} />
+ </View>
+ ))}
+ <TouchableOpacity
+ style={panelButtonStyle}
+ onPress={() => props.setIsOpen(false)}>
+ {/* a dummy icon for aligning the cancel button */}
+ {showIcons && <View style={styles.icon} />}
+ <Text style={styles.panelButtonTitleCancel}>Cancel</Text>
+ </TouchableOpacity>
+ </View>
+ </BottomDrawer>
+ );
+};
+
+const styles = StyleSheet.create({
+ panel: {
+ height: SCREEN_HEIGHT,
+ backgroundColor: 'white',
+ borderTopLeftRadius: 20,
+ borderTopRightRadius: 20,
+ },
+ panelButtonTitle: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ color: 'black',
+ },
+ icon: {
+ height: 25,
+ width: 25,
+ marginLeft: SCREEN_WIDTH * 0.3,
+ marginRight: 25,
+ },
+ panelButtonTitleCancel: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ color: TAGG_TEXT_LIGHT_BLUE,
+ },
+ divider: {height: 1, borderWidth: 1, borderColor: '#e7e7e7'},
+});
+
+export default GenericMoreInfoDrawer;
diff --git a/src/components/common/index.ts b/src/components/common/index.ts
index f6521497..661d2f52 100644
--- a/src/components/common/index.ts
+++ b/src/components/common/index.ts
@@ -16,3 +16,4 @@ export {default as PostCarousel} from './PostCarousel';
export {default as TaggDatePicker} from './TaggDatePicker';
export {default as BottomDrawer} from './BottomDrawer';
export {default as TaggLoadingTndicator} from './TaggLoadingIndicator';
+export {default as GenericMoreInfoDrawer} from './GenericMoreInfoDrawer';
diff --git a/src/components/profile/MomentMoreInfoDrawer.tsx b/src/components/profile/MomentMoreInfoDrawer.tsx
new file mode 100644
index 00000000..18462cbb
--- /dev/null
+++ b/src/components/profile/MomentMoreInfoDrawer.tsx
@@ -0,0 +1,58 @@
+import React from 'react';
+import {Alert, TouchableOpacity} from 'react-native';
+import MoreIcon from '../../assets/icons/more_horiz-24px.svg';
+import {deleteMoment} from '../../services';
+import {GenericMoreInfoDrawer} from '../common';
+
+interface MomentMoreInfoDrawerProps {
+ isOpen: boolean;
+ setIsOpen: (visible: boolean) => void;
+ momentId: string;
+ dismissScreenAndUpdate: Function;
+}
+
+const MomentMoreInfoDrawer: React.FC<MomentMoreInfoDrawerProps> = (props) => {
+ const {momentId, setIsOpen, dismissScreenAndUpdate} = props;
+
+ const handleDeleteMoment = async () => {
+ setIsOpen(false);
+ deleteMoment(momentId).then((success) => {
+ if (success) {
+ // set time out for UI transitions
+ setTimeout(() => {
+ Alert.alert('Moment deleted!', '', [
+ {
+ text: 'OK',
+ onPress: () => dismissScreenAndUpdate(),
+ style: 'cancel',
+ },
+ ]);
+ }, 500);
+ } else {
+ setTimeout(() => {
+ Alert.alert(
+ 'We were unable to delete that moment 😭 , please try again later!',
+ );
+ }, 500);
+ }
+ });
+ };
+
+ return (
+ <>
+ <TouchableOpacity
+ onPress={() => {
+ setIsOpen(true);
+ }}>
+ <MoreIcon height={30} width={30} color={'white'} />
+ </TouchableOpacity>
+ <GenericMoreInfoDrawer
+ {...props}
+ showIcons={false}
+ buttons={[['Delete Moment', handleDeleteMoment]]}
+ />
+ </>
+ );
+};
+
+export default MomentMoreInfoDrawer;
diff --git a/src/components/profile/MoreInfoDrawer.tsx b/src/components/profile/MoreInfoDrawer.tsx
deleted file mode 100644
index a8908b4d..00000000
--- a/src/components/profile/MoreInfoDrawer.tsx
+++ /dev/null
@@ -1,88 +0,0 @@
-import {useNavigation} from '@react-navigation/native';
-import React, {useContext} from 'react';
-import {StyleSheet, Text, TouchableOpacity, View} from 'react-native';
-import {useSafeAreaInsets} from 'react-native-safe-area-context';
-import {TAGG_TEXT_LIGHT_BLUE} from '../../constants';
-import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
-import {BottomDrawer} from '../common';
-import PersonOutline from '../../assets/ionicons/person-outline.svg';
-import {useSelector} from 'react-redux';
-import {RootState} from '../../store/rootreducer';
-
-interface MoreInfoDrawerProps {
- isOpen: boolean;
- setIsOpen: (visible: boolean) => void;
-}
-
-const MoreInfoDrawer: React.FC<MoreInfoDrawerProps> = (props) => {
- const insets = useSafeAreaInsets();
- const initialSnapPosition = 160 + insets.bottom;
- const navigation = useNavigation();
- const {
- user: {userId, username},
- } = useSelector((state: RootState) => state.user);
-
- const goToEditProfile = () => {
- navigation.push('EditProfile', {
- userId: userId,
- username: username,
- });
- props.setIsOpen(false);
- };
-
- return (
- <BottomDrawer
- {...props}
- showHeader={false}
- initialSnapPosition={initialSnapPosition}>
- <View style={styles.panel}>
- <TouchableOpacity style={styles.panelButton} onPress={goToEditProfile}>
- <PersonOutline style={styles.icon} />
- <Text style={styles.panelButtonTitle}>Edit Profile</Text>
- </TouchableOpacity>
- <View style={styles.divider} />
- <TouchableOpacity
- style={styles.panelButton}
- onPress={() => props.setIsOpen(false)}>
- {/* Just a placeholder "icon" for easier alignment */}
- <View style={styles.icon} />
- <Text style={styles.panelButtonTitleCancel}>Cancel</Text>
- </TouchableOpacity>
- </View>
- </BottomDrawer>
- );
-};
-
-const styles = StyleSheet.create({
- panel: {
- height: SCREEN_HEIGHT,
- backgroundColor: 'white',
- borderTopLeftRadius: 20,
- borderTopRightRadius: 20,
- },
- panelButton: {
- height: 80,
- flexDirection: 'row',
- alignItems: 'center',
- },
- panelButtonTitle: {
- fontSize: 18,
- fontWeight: 'bold',
- color: 'black',
- },
- icon: {
- height: 25,
- width: 25,
- color: 'black',
- marginLeft: SCREEN_WIDTH * 0.3,
- marginRight: 25,
- },
- panelButtonTitleCancel: {
- fontSize: 18,
- fontWeight: 'bold',
- color: TAGG_TEXT_LIGHT_BLUE,
- },
- divider: {height: 1, borderWidth: 1, borderColor: '#e7e7e7'},
-});
-
-export default MoreInfoDrawer;
diff --git a/src/components/profile/ProfileHeader.tsx b/src/components/profile/ProfileHeader.tsx
index 621aae9a..677728d2 100644
--- a/src/components/profile/ProfileHeader.tsx
+++ b/src/components/profile/ProfileHeader.tsx
@@ -1,14 +1,12 @@
-import React, {useState, useContext} from 'react';
-import {StyleSheet, Text, TouchableOpacity, View} from 'react-native';
-import MoreIcon from '../../assets/icons/more_horiz-24px.svg';
-import {TAGG_DARK_BLUE} from '../../constants';
-import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
-import Avatar from './Avatar';
-import MoreInfoDrawer from './MoreInfoDrawer';
-import FollowCount from './FollowCount';
+import React, {useState} from 'react';
+import {StyleSheet, Text, View} from 'react-native';
import {useSelector} from 'react-redux';
import {RootState} from '../../store/rootreducer';
import {ScreenType} from '../../types';
+import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
+import Avatar from './Avatar';
+import FollowCount from './FollowCount';
+import ProfileMoreInfoDrawer from './ProfileMoreInfoDrawer';
type ProfileHeaderProps = {
userXId: string;
@@ -25,16 +23,10 @@ const ProfileHeader: React.FC<ProfileHeaderProps> = ({userXId, screenType}) => {
return (
<View style={styles.container}>
{!userXId && (
- <>
- <TouchableOpacity
- style={styles.more}
- onPress={() => {
- setDrawerVisible(true);
- }}>
- <MoreIcon height={30} width={30} color={TAGG_DARK_BLUE} />
- </TouchableOpacity>
- <MoreInfoDrawer isOpen={drawerVisible} setIsOpen={setDrawerVisible} />
- </>
+ <ProfileMoreInfoDrawer
+ isOpen={drawerVisible}
+ setIsOpen={setDrawerVisible}
+ />
)}
<View style={styles.row}>
<Avatar
@@ -91,12 +83,6 @@ const styles = StyleSheet.create({
follows: {
marginHorizontal: SCREEN_HEIGHT / 50,
},
- more: {
- position: 'absolute',
- right: '5%',
- marginTop: '4%',
- zIndex: 1,
- },
});
export default ProfileHeader;
diff --git a/src/components/profile/ProfileMoreInfoDrawer.tsx b/src/components/profile/ProfileMoreInfoDrawer.tsx
new file mode 100644
index 00000000..4fe24128
--- /dev/null
+++ b/src/components/profile/ProfileMoreInfoDrawer.tsx
@@ -0,0 +1,90 @@
+import {useNavigation} from '@react-navigation/native';
+import React from 'react';
+import {StyleSheet, TouchableOpacity} from 'react-native';
+import {useSelector} from 'react-redux';
+import MoreIcon from '../../assets/icons/more_horiz-24px.svg';
+import PersonOutline from '../../assets/ionicons/person-outline.svg';
+import {TAGG_DARK_BLUE, TAGG_TEXT_LIGHT_BLUE} from '../../constants';
+import {RootState} from '../../store/rootreducer';
+import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
+import {GenericMoreInfoDrawer} from '../common';
+
+interface ProfileMoreInfoDrawerProps {
+ isOpen: boolean;
+ setIsOpen: (visible: boolean) => void;
+}
+
+const ProfileMoreInfoDrawer: React.FC<ProfileMoreInfoDrawerProps> = (props) => {
+ const {setIsOpen} = props;
+ const navigation = useNavigation();
+ const {
+ user: {userId, username},
+ } = useSelector((state: RootState) => state.user);
+
+ const goToEditProfile = () => {
+ navigation.push('EditProfile', {
+ userId: userId,
+ username: username,
+ });
+ setIsOpen(false);
+ };
+
+ return (
+ <>
+ <TouchableOpacity
+ style={styles.more}
+ onPress={() => {
+ setIsOpen(true);
+ }}>
+ <MoreIcon height={30} width={30} color={TAGG_DARK_BLUE} />
+ </TouchableOpacity>
+ <GenericMoreInfoDrawer
+ {...props}
+ showIcons={true}
+ buttons={[
+ ['Edit Profile', goToEditProfile, <PersonOutline color="black" />],
+ ]}
+ />
+ </>
+ );
+};
+
+const styles = StyleSheet.create({
+ panel: {
+ height: SCREEN_HEIGHT,
+ backgroundColor: 'white',
+ borderTopLeftRadius: 20,
+ borderTopRightRadius: 20,
+ },
+ panelButton: {
+ height: 80,
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ panelButtonTitle: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ color: 'black',
+ },
+ icon: {
+ height: 25,
+ width: 25,
+ color: 'black',
+ marginLeft: SCREEN_WIDTH * 0.3,
+ marginRight: 25,
+ },
+ panelButtonTitleCancel: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ color: TAGG_TEXT_LIGHT_BLUE,
+ },
+ divider: {height: 1, borderWidth: 1, borderColor: '#e7e7e7'},
+ more: {
+ position: 'absolute',
+ right: '5%',
+ marginTop: '4%',
+ zIndex: 1,
+ },
+});
+
+export default ProfileMoreInfoDrawer;
diff --git a/src/components/profile/index.ts b/src/components/profile/index.ts
index 0f57347b..dc3872b1 100644
--- a/src/components/profile/index.ts
+++ b/src/components/profile/index.ts
@@ -5,4 +5,5 @@ export {default as ProfileBody} from './ProfileBody';
export {default as ProfileHeader} from './ProfileHeader';
export {default as ProfilePreview} from './ProfilePreview';
export {default as Followers} from './Followers';
-export {default as MoreInfoDrawer} from './MoreInfoDrawer';
+export {default as ProfileMoreInfoDrawer} from './ProfileMoreInfoDrawer';
+export {default as MomentMoreInfoDrawer} from './MomentMoreInfoDrawer';
diff --git a/src/screens/profile/EditProfile.tsx b/src/screens/profile/EditProfile.tsx
index 00a043cd..ab58db41 100644
--- a/src/screens/profile/EditProfile.tsx
+++ b/src/screens/profile/EditProfile.tsx
@@ -31,7 +31,7 @@ import {
} from '../../constants';
import AsyncStorage from '@react-native-community/async-storage';
import Animated from 'react-native-reanimated';
-import {SCREEN_HEIGHT} from '../../utils';
+import {HeaderHeight, SCREEN_HEIGHT} from '../../utils';
import {RootState} from '../../store/rootReducer';
import {useDispatch, useSelector} from 'react-redux';
import {loadUserData} from '../../store/actions';
@@ -70,7 +70,9 @@ const ProfileOnboarding: React.FC<ProfileOnboardingProps> = ({
const dispatch = useDispatch();
useEffect(() => {
- if (needsUpdate) dispatch(loadUserData({userId, username}));
+ if (needsUpdate) {
+ dispatch(loadUserData({userId, username}));
+ }
}, [loadUserData, needsUpdate]);
const [isCustomGender, setIsCustomGender] = React.useState<boolean>(
@@ -502,7 +504,7 @@ const ProfileOnboarding: React.FC<ProfileOnboardingProps> = ({
const styles = StyleSheet.create({
container: {
- marginTop: '10%',
+ marginTop: HeaderHeight,
flex: 1,
flexDirection: 'column',
width: '100%',
diff --git a/src/screens/profile/IndividualMoment.tsx b/src/screens/profile/IndividualMoment.tsx
index 2739324e..d1b21d0f 100644
--- a/src/screens/profile/IndividualMoment.tsx
+++ b/src/screens/profile/IndividualMoment.tsx
@@ -1,34 +1,30 @@
-import React, {useEffect, useState} from 'react';
-import {StyleSheet, View, Image, Text} from 'react-native';
-import {Button} from 'react-native-elements';
+import AsyncStorage from '@react-native-community/async-storage';
import {BlurView} from '@react-native-community/blur';
-import {
- SCREEN_HEIGHT,
- SCREEN_WIDTH,
- StatusBarHeight,
- getTimePosted,
-} from '../../utils';
-import {UserType} from '../../types';
import {RouteProp} from '@react-navigation/native';
import {StackNavigationProp} from '@react-navigation/stack';
-import {CaptionScreenHeader} from '../../components';
-import {ProfileStackParams} from 'src/routes/profile/ProfileStack';
+import React, {useEffect, useState} from 'react';
+import {Alert, Image, StyleSheet, View} from 'react-native';
+import {Button} from 'react-native-elements';
+import {TouchableOpacity} from 'react-native-gesture-handler';
import Animated from 'react-native-reanimated';
-import {CommentsCount} from '../../components';
-import AsyncStorage from '@react-native-community/async-storage';
+import {useDispatch, useSelector} from 'react-redux';
+import {ProfileStackParams} from 'src/routes/profile/ProfileStack';
+import {
+ CaptionScreenHeader,
+ CommentsCount,
+ MomentMoreInfoDrawer,
+} from '../../components';
import {getMomentCommentsCount} from '../../services';
-import {TouchableOpacity} from 'react-native-gesture-handler';
-import {Alert} from 'react-native';
import {sendReport} from '../../services/ReportingService';
-import {logout} from '../../store/actions';
-import {useDispatch, useSelector} from 'react-redux';
-import {RootState} from '../../store/rootreducer';
+import {loadUserMoments, logout} from '../../store/actions';
import {DUMMY_USERNAME} from '../../store/initialStates';
-
-const NO_USER: UserType = {
- userId: '',
- username: '',
-};
+import {RootState} from '../../store/rootreducer';
+import {
+ getTimePosted,
+ SCREEN_HEIGHT,
+ SCREEN_WIDTH,
+ StatusBarHeight,
+} from '../../utils';
/**
* Individual moment view opened when user clicks on a moment tile
@@ -59,7 +55,7 @@ const IndividualMoment: React.FC<IndividualMomentProps> = ({
const {userXId, screenType} = route.params;
const {
- user: {userId: loggedInUserId, username: loggedInusername},
+ user: {userId: loggedInUserId, username: loggedInUsername},
} = useSelector((state: RootState) => state.user);
const {
@@ -68,22 +64,14 @@ const IndividualMoment: React.FC<IndividualMomentProps> = ({
? useSelector((state: RootState) => state.userX[screenType][userXId])
: {user: {username: DUMMY_USERNAME}};
- const isOwnProfile = username === loggedInusername;
- const [user, setUser] = useState<UserType>(NO_USER);
- const [caption, setCaption] = React.useState(route.params.moment.caption);
+ const isOwnProfile = username === loggedInUsername;
const [elapsedTime, setElapsedTime] = React.useState<string>();
const [comments_count, setCommentsCount] = React.useState('');
const [isReporting, setIsReporting] = React.useState(false);
const dispatch = useDispatch();
-
- const handleCaptionUpdate = (caption: string) => {
- setCaption(caption);
- };
+ const [drawerVisible, setDrawerVisible] = useState(false);
useEffect(() => {
- if (!loggedInUserId) {
- setUser(NO_USER);
- }
const timePeriod = async () => {
setElapsedTime(getTimePosted(date_time));
};
@@ -99,7 +87,7 @@ const IndividualMoment: React.FC<IndividualMomentProps> = ({
timePeriod();
loadComments();
- }, [date_time, loggedInUserId]);
+ }, [date_time, dispatch, loggedInUserId, moment_id]);
const sendReportAlert = async () => {
const token = await AsyncStorage.getItem('token');
@@ -152,6 +140,17 @@ const IndividualMoment: React.FC<IndividualMomentProps> = ({
navigation.pop();
}}
/>
+ {!userXId && (
+ <MomentMoreInfoDrawer
+ isOpen={drawerVisible}
+ setIsOpen={setDrawerVisible}
+ momentId={moment_id}
+ dismissScreenAndUpdate={() => {
+ dispatch(loadUserMoments(loggedInUserId));
+ navigation.pop();
+ }}
+ />
+ )}
</View>
<CaptionScreenHeader
style={styles.header}
@@ -171,7 +170,9 @@ const IndividualMoment: React.FC<IndividualMomentProps> = ({
/>
<Animated.Text style={styles.text}>{elapsedTime}</Animated.Text>
</View>
- <Animated.Text style={styles.captionText}>{caption}</Animated.Text>
+ <Animated.Text style={styles.captionText}>
+ {route.params.moment.caption}
+ </Animated.Text>
{userXId && !isOwnProfile && !isReporting && (
<TouchableOpacity onPress={sendReportAlert}>
<Animated.Text style={styles.reportIssue}>
diff --git a/src/services/MomentServices.ts b/src/services/MomentServices.ts
index 46ca1351..ed868582 100644
--- a/src/services/MomentServices.ts
+++ b/src/services/MomentServices.ts
@@ -1,6 +1,6 @@
-//Common moments api abstracted out here
-import {COMMENTS_ENDPOINT, MOMENTS_ENDPOINT} from '../constants';
+import AsyncStorage from '@react-native-community/async-storage';
import {Alert} from 'react-native';
+import {COMMENTS_ENDPOINT, MOMENTS_ENDPOINT} from '../constants';
import {MomentType} from '../types';
//Get all comments for a moment
@@ -123,3 +123,21 @@ export const loadMoments: (
}
return moments;
};
+
+export const deleteMoment = async (momentId: string) => {
+ try {
+ const token = await AsyncStorage.getItem('token');
+
+ const response = await fetch(MOMENTS_ENDPOINT + `${momentId}/`, {
+ method: 'DELETE',
+ headers: {
+ Authorization: 'Token ' + token,
+ },
+ });
+ return response.status === 200;
+ } catch (error) {
+ console.log(error);
+ console.log('Unable to delete moment', momentId);
+ return false;
+ }
+};