diff options
-rw-r--r-- | src/components/common/GenericMoreInfoDrawer.tsx | 93 | ||||
-rw-r--r-- | src/components/common/index.ts | 1 | ||||
-rw-r--r-- | src/components/profile/MomentMoreInfoDrawer.tsx | 58 | ||||
-rw-r--r-- | src/components/profile/MoreInfoDrawer.tsx | 88 | ||||
-rw-r--r-- | src/components/profile/ProfileHeader.tsx | 34 | ||||
-rw-r--r-- | src/components/profile/ProfileMoreInfoDrawer.tsx | 90 | ||||
-rw-r--r-- | src/components/profile/index.ts | 3 | ||||
-rw-r--r-- | src/screens/profile/EditProfile.tsx | 8 | ||||
-rw-r--r-- | src/screens/profile/IndividualMoment.tsx | 75 | ||||
-rw-r--r-- | src/services/MomentServices.ts | 22 |
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; + } +}; |