From 84616a6143fea58899eafa915a73592eaad25361 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Mon, 7 Dec 2020 19:19:50 -0500 Subject: [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 --- src/components/common/GenericMoreInfoDrawer.tsx | 93 ++++++++++++++++++++++++ src/components/common/index.ts | 1 + src/components/profile/MomentMoreInfoDrawer.tsx | 58 +++++++++++++++ src/components/profile/MoreInfoDrawer.tsx | 88 ---------------------- src/components/profile/ProfileHeader.tsx | 34 +++------ src/components/profile/ProfileMoreInfoDrawer.tsx | 90 +++++++++++++++++++++++ src/components/profile/index.ts | 3 +- src/screens/profile/EditProfile.tsx | 8 +- src/screens/profile/IndividualMoment.tsx | 75 +++++++++---------- src/services/MomentServices.ts | 22 +++++- 10 files changed, 317 insertions(+), 155 deletions(-) create mode 100644 src/components/common/GenericMoreInfoDrawer.tsx create mode 100644 src/components/profile/MomentMoreInfoDrawer.tsx delete mode 100644 src/components/profile/MoreInfoDrawer.tsx create mode 100644 src/components/profile/ProfileMoreInfoDrawer.tsx (limited to 'src') 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 = (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 ( + + + {buttons.map(([title, action, icon], index) => ( + + + {showIcons && {icon}} + {title} + + + + ))} + props.setIsOpen(false)}> + {/* a dummy icon for aligning the cancel button */} + {showIcons && } + Cancel + + + + ); +}; + +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 = (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 ( + <> + { + setIsOpen(true); + }}> + + + + + ); +}; + +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 = (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 ( - - - - - Edit Profile - - - props.setIsOpen(false)}> - {/* Just a placeholder "icon" for easier alignment */} - - Cancel - - - - ); -}; - -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 = ({userXId, screenType}) => { return ( {!userXId && ( - <> - { - setDrawerVisible(true); - }}> - - - - + )} void; +} + +const ProfileMoreInfoDrawer: React.FC = (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 ( + <> + { + setIsOpen(true); + }}> + + + ], + ]} + /> + + ); +}; + +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 = ({ const dispatch = useDispatch(); useEffect(() => { - if (needsUpdate) dispatch(loadUserData({userId, username})); + if (needsUpdate) { + dispatch(loadUserData({userId, username})); + } }, [loadUserData, needsUpdate]); const [isCustomGender, setIsCustomGender] = React.useState( @@ -502,7 +504,7 @@ const ProfileOnboarding: React.FC = ({ 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 = ({ 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 = ({ ? useSelector((state: RootState) => state.userX[screenType][userXId]) : {user: {username: DUMMY_USERNAME}}; - const isOwnProfile = username === loggedInusername; - const [user, setUser] = useState(NO_USER); - const [caption, setCaption] = React.useState(route.params.moment.caption); + const isOwnProfile = username === loggedInUsername; const [elapsedTime, setElapsedTime] = React.useState(); 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 = ({ 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 = ({ navigation.pop(); }} /> + {!userXId && ( + { + dispatch(loadUserMoments(loggedInUserId)); + navigation.pop(); + }} + /> + )} = ({ /> {elapsedTime} - {caption} + + {route.params.moment.caption} + {userXId && !isOwnProfile && !isReporting && ( 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; + } +}; -- cgit v1.2.3-70-g09d2