diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/components/common/GenericMoreInfoDrawer.tsx | 14 | ||||
-rw-r--r-- | src/components/moments/MomentPost.tsx | 8 | ||||
-rw-r--r-- | src/components/moments/MomentPostContent.tsx | 24 | ||||
-rw-r--r-- | src/components/moments/MomentPostHeader.tsx | 12 | ||||
-rw-r--r-- | src/components/profile/MomentMoreInfoDrawer.tsx | 90 | ||||
-rw-r--r-- | src/components/profile/ProfileMoreInfoDrawer.tsx | 5 | ||||
-rw-r--r-- | src/routes/main/MainStackNavigator.tsx | 8 | ||||
-rw-r--r-- | src/screens/moments/TagFriendsScreen.tsx | 4 | ||||
-rw-r--r-- | src/screens/profile/CaptionScreen.tsx | 90 | ||||
-rw-r--r-- | src/services/MomentService.ts | 32 |
10 files changed, 184 insertions, 103 deletions
diff --git a/src/components/common/GenericMoreInfoDrawer.tsx b/src/components/common/GenericMoreInfoDrawer.tsx index 0928ed44..cfc45131 100644 --- a/src/components/common/GenericMoreInfoDrawer.tsx +++ b/src/components/common/GenericMoreInfoDrawer.tsx @@ -3,15 +3,16 @@ import { GestureResponderEvent, StyleSheet, Text, + TextStyle, TouchableOpacity, View, ViewProps, ViewStyle, } from 'react-native'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; -import BottomDrawer from './BottomDrawer'; import {TAGG_LIGHT_BLUE} from '../../constants'; import {normalize, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; +import BottomDrawer from './BottomDrawer'; // conforms the JSX onPress attribute type type OnPressHandler = (event: GestureResponderEvent) => void; @@ -20,13 +21,12 @@ interface GenericMoreInfoDrawerProps extends ViewProps { isOpen: boolean; setIsOpen: (visible: boolean) => void; showIcons: boolean; - textColor: string; // An array of title, onPressHandler, and icon component - buttons: [string, OnPressHandler, JSX.Element?][]; + buttons: [string, OnPressHandler, JSX.Element?, TextStyle?][]; } const GenericMoreInfoDrawer: React.FC<GenericMoreInfoDrawerProps> = (props) => { - const {buttons, showIcons, textColor} = props; + const {buttons, showIcons} = props; // each button is 80px high, cancel button is always there const initialSnapPosition = (buttons.length + 1) * 80 + useSafeAreaInsets().bottom; @@ -44,13 +44,11 @@ const GenericMoreInfoDrawer: React.FC<GenericMoreInfoDrawerProps> = (props) => { showHeader={false} initialSnapPosition={initialSnapPosition}> <View style={styles.panel}> - {buttons.map(([title, action, icon], index) => ( + {buttons.map(([title, action, icon, textStyle], index) => ( <View key={index}> <TouchableOpacity style={panelButtonStyle} onPress={action}> {showIcons && <View style={styles.icon}>{icon}</View>} - <Text style={[styles.panelButtonTitle, {color: textColor}]}> - {title} - </Text> + <Text style={[styles.panelButtonTitle, textStyle]}>{title}</Text> </TouchableOpacity> <View style={styles.divider} /> </View> diff --git a/src/components/moments/MomentPost.tsx b/src/components/moments/MomentPost.tsx index 7149a5b4..e744fcd9 100644 --- a/src/components/moments/MomentPost.tsx +++ b/src/components/moments/MomentPost.tsx @@ -76,18 +76,16 @@ const MomentPost: React.FC<MomentPostProps> = ({item, userXId, screenType}) => { userXId={userXId} screenType={screenType} username={isOwnProfile ? loggedInUsername : username} - momentId={item.moment_id} style={styles.postHeader} momentTagId={momentTagId} removeTag={removeTag} + moment={item} + tags={tags} /> <MomentPostContent style={styles.postContent} - momentId={item.moment_id} - caption={item.caption} - pathHash={item.moment_url} - dateTime={item.date_created} screenType={screenType} + moment={item} momentTags={tags} /> </View> diff --git a/src/components/moments/MomentPostContent.tsx b/src/components/moments/MomentPostContent.tsx index ecbfb3a2..d831d7ee 100644 --- a/src/components/moments/MomentPostContent.tsx +++ b/src/components/moments/MomentPostContent.tsx @@ -6,7 +6,7 @@ import Animated, {Easing} from 'react-native-reanimated'; import {useDispatch, useStore} from 'react-redux'; import {getCommentsCount} from '../../services'; import {RootState} from '../../store/rootReducer'; -import {MomentTagType, ScreenType, UserType} from '../../types'; +import {MomentTagType, MomentType, ScreenType, UserType} from '../../types'; import { getTimePosted, navigateToProfile, @@ -20,19 +20,13 @@ import {MomentTags} from '../common'; interface MomentPostContentProps extends ViewProps { screenType: ScreenType; - momentId: string; - caption: string; - pathHash: string; - dateTime: string; + moment: MomentType; momentTags: MomentTagType[]; } const MomentPostContent: React.FC<MomentPostContentProps> = ({ screenType, - momentId, - caption, - pathHash, - dateTime, style, + moment, momentTags, }) => { const [elapsedTime, setElapsedTime] = useState(''); @@ -54,12 +48,12 @@ const MomentPostContent: React.FC<MomentPostContentProps> = ({ useEffect(() => { const fetchCommentsCount = async () => { - const count = await getCommentsCount(momentId, false); + const count = await getCommentsCount(moment.moment_id, false); setCommentsCount(count); }; - setElapsedTime(getTimePosted(dateTime)); + setElapsedTime(getTimePosted(moment.date_created)); fetchCommentsCount(); - }, [dateTime, momentId]); + }, [moment.date_created, moment.moment_id]); useEffect(() => { const fade = async () => { @@ -82,7 +76,7 @@ const MomentPostContent: React.FC<MomentPostContentProps> = ({ <Image ref={imageRef} style={styles.image} - source={{uri: pathHash}} + source={{uri: moment.moment_url}} resizeMode={'cover'} /> {tags.length > 0 && ( @@ -100,13 +94,13 @@ const MomentPostContent: React.FC<MomentPostContentProps> = ({ <View style={styles.footerContainer}> <CommentsCount commentsCount={comments_count} - momentId={momentId} + momentId={moment.moment_id} screenType={screenType} /> <Text style={styles.text}>{elapsedTime}</Text> </View> {renderTextWithMentions({ - value: caption, + value: moment.caption, styles: styles.captionText, partTypes: mentionPartTypes('white'), onPress: (user: UserType) => diff --git a/src/components/moments/MomentPostHeader.tsx b/src/components/moments/MomentPostHeader.tsx index dc6a3cd9..cde7639c 100644 --- a/src/components/moments/MomentPostHeader.tsx +++ b/src/components/moments/MomentPostHeader.tsx @@ -10,7 +10,7 @@ import { import {useDispatch, useSelector, useStore} from 'react-redux'; import {loadUserMoments} from '../../store/actions'; import {RootState} from '../../store/rootReducer'; -import {ScreenType} from '../../types'; +import {MomentTagType, MomentType, ScreenType} from '../../types'; import {fetchUserX, userXInStore} from '../../utils'; import {MomentMoreInfoDrawer} from '../profile'; import TaggAvatar from '../profile/TaggAvatar'; @@ -19,19 +19,21 @@ interface MomentPostHeaderProps extends ViewProps { userXId?: string; screenType: ScreenType; username: string; - momentId: string; momentTagId: string; removeTag: () => Promise<void>; + moment: MomentType; + tags: MomentTagType[]; } const MomentPostHeader: React.FC<MomentPostHeaderProps> = ({ userXId, screenType, username, - momentId, style, momentTagId, removeTag, + moment, + tags, }) => { const [drawerVisible, setDrawerVisible] = useState(false); const dispatch = useDispatch(); @@ -68,7 +70,6 @@ const MomentPostHeader: React.FC<MomentPostHeaderProps> = ({ <MomentMoreInfoDrawer isOpen={drawerVisible} setIsOpen={setDrawerVisible} - momentId={momentId} isOwnProfile={isOwnProfile} momentTagId={momentTagId} removeTag={removeTag} @@ -76,6 +77,9 @@ const MomentPostHeader: React.FC<MomentPostHeaderProps> = ({ dispatch(loadUserMoments(loggedInUserId)); navigation.pop(); }} + screenType={screenType} + moment={moment} + tags={tags} /> </View> ); diff --git a/src/components/profile/MomentMoreInfoDrawer.tsx b/src/components/profile/MomentMoreInfoDrawer.tsx index 1265497e..a796ffd8 100644 --- a/src/components/profile/MomentMoreInfoDrawer.tsx +++ b/src/components/profile/MomentMoreInfoDrawer.tsx @@ -1,44 +1,58 @@ +import {useNavigation} from '@react-navigation/core'; import React, {useEffect, useState} from 'react'; import { Alert, GestureResponderEvent, StyleSheet, + TextStyle, TouchableOpacity, ViewProps, } from 'react-native'; import MoreIcon from '../../assets/icons/more_horiz-24px.svg'; import {ERROR_DELETE_MOMENT, MOMENT_DELETED_MSG} from '../../constants/strings'; import {deleteMoment, sendReport} from '../../services'; +import {MomentTagType, MomentType, ScreenType} from '../../types/types'; import {GenericMoreInfoDrawer} from '../common'; enum MomentDrawerOptions { DeleteMoment = 'Delete Moment', ReportIssue = 'Report an Issue', RemoveTag = 'Remove yourself from moment', + EditMoment = 'Edit Moment', } interface MomentMoreInfoDrawerProps extends ViewProps { isOpen: boolean; setIsOpen: (visible: boolean) => void; - momentId: string; isOwnProfile: boolean; momentTagId: string; removeTag: () => Promise<void>; dismissScreenAndUpdate: () => void; + screenType: ScreenType; + moment: MomentType; + tags: MomentTagType[]; } const MomentMoreInfoDrawer: React.FC<MomentMoreInfoDrawerProps> = (props) => { const { - momentId, setIsOpen, isOwnProfile, dismissScreenAndUpdate, momentTagId, removeTag, + screenType, + moment, + tags, } = props; + const navigation = useNavigation(); + + const [drawerButtons, setDrawerButtons] = useState< + [string, (event: GestureResponderEvent) => void, JSX.Element?, TextStyle?][] + >([]); + const handleDeleteMoment = async () => { setIsOpen(false); - deleteMoment(momentId).then((success) => { + deleteMoment(moment.moment_id).then((success) => { if (success) { // set time out for UI transitions setTimeout(() => { @@ -88,7 +102,8 @@ const MomentMoreInfoDrawer: React.FC<MomentMoreInfoDrawerProps> = (props) => { [ { text: 'Mark as inappropriate', - onPress: () => sendReport(momentId, 'Mark as inappropriate'), + onPress: () => + sendReport(moment.moment_id, 'Mark as inappropriate'), }, { text: 'Cancel', @@ -96,7 +111,7 @@ const MomentMoreInfoDrawer: React.FC<MomentMoreInfoDrawerProps> = (props) => { }, { text: 'Mark as abusive', - onPress: () => sendReport(momentId, 'Mark as abusive'), + onPress: () => sendReport(moment.moment_id, 'Mark as abusive'), }, ], {cancelable: false}, @@ -104,42 +119,52 @@ const MomentMoreInfoDrawer: React.FC<MomentMoreInfoDrawerProps> = (props) => { }, 500); }; - const [drawerButtons, setDrawerButtons] = useState< - [string, (event: GestureResponderEvent) => void, JSX.Element?][] - >([ - isOwnProfile - ? [MomentDrawerOptions.DeleteMoment, handleDeleteMoment] - : [MomentDrawerOptions.ReportIssue, handleReportMoment], - ]); + const handleEditMoment = async () => { + setIsOpen(false); + navigation.navigate('CaptionScreen', { + screenType: screenType, + selectedTags: tags, + moment: moment, + }); + }; /* * Update bottom drawer options to contain/not contain 'remove tag' option */ useEffect(() => { - const setupBottomDrawer = () => { - const present = drawerButtons.findIndex( - (button) => button[0] === MomentDrawerOptions.RemoveTag, - ); - /* - * If user is not tagged but button is present, remove button from bottom drawer - * If user is tagged but button is not present, add button to bottom drawer - */ - if (momentTagId !== '' && present === -1) { - const localDrawerButtons = drawerButtons; - localDrawerButtons.push([ + let newButtons: [ + string, + (event: GestureResponderEvent) => void, + JSX.Element?, + TextStyle?, + ][] = []; + if (!isOwnProfile) { + newButtons.push([ + MomentDrawerOptions.ReportIssue, + handleReportMoment, + undefined, + {color: 'red'}, + ]); + // should we have the "delete moment" option? + if (momentTagId !== '') { + newButtons.push([ MomentDrawerOptions.RemoveTag, handleRemoveTag, + undefined, + {color: 'red'}, ]); - setDrawerButtons(localDrawerButtons); - } else if (momentTagId === '' && present !== -1) { - const filteredButtons = drawerButtons.filter( - (button) => button[0] !== MomentDrawerOptions.RemoveTag, - ); - setDrawerButtons(filteredButtons); } - }; - setupBottomDrawer(); - }, [momentTagId]); + } else { + newButtons.push([ + MomentDrawerOptions.DeleteMoment, + handleDeleteMoment, + undefined, + {color: 'red'}, + ]); + newButtons.push([MomentDrawerOptions.EditMoment, handleEditMoment]); + } + setDrawerButtons(newButtons); + }, [tags, momentTagId]); return ( <> @@ -153,7 +178,6 @@ const MomentMoreInfoDrawer: React.FC<MomentMoreInfoDrawerProps> = (props) => { <GenericMoreInfoDrawer {...props} showIcons={false} - textColor={'red'} buttons={drawerButtons} /> </> diff --git a/src/components/profile/ProfileMoreInfoDrawer.tsx b/src/components/profile/ProfileMoreInfoDrawer.tsx index ecc45211..656f81bb 100644 --- a/src/components/profile/ProfileMoreInfoDrawer.tsx +++ b/src/components/profile/ProfileMoreInfoDrawer.tsx @@ -55,12 +55,12 @@ const ProfileMoreInfoDrawer: React.FC<ProfileMoreInfoDrawerProps> = (props) => { <GenericMoreInfoDrawer {...props} showIcons={false} - textColor={'red'} buttons={[ [ (isBlocked ? 'Unblock' : 'Block') + ` ${userXName}`, onBlockUnblock, undefined, + {color: 'red'}, ], ]} /> @@ -68,7 +68,6 @@ const ProfileMoreInfoDrawer: React.FC<ProfileMoreInfoDrawerProps> = (props) => { <GenericMoreInfoDrawer {...props} showIcons={true} - textColor={'black'} buttons={[ [ 'Settings', @@ -77,6 +76,7 @@ const ProfileMoreInfoDrawer: React.FC<ProfileMoreInfoDrawerProps> = (props) => { source={require('../../assets/images/settings/settings.png')} style={styles.image} />, + {color: 'black'}, ], [ 'Edit Profile', @@ -85,6 +85,7 @@ const ProfileMoreInfoDrawer: React.FC<ProfileMoreInfoDrawerProps> = (props) => { source={require('../../assets/images/settings/edit-profile.png')} style={styles.image} />, + {color: 'black'}, ], ]} /> diff --git a/src/routes/main/MainStackNavigator.tsx b/src/routes/main/MainStackNavigator.tsx index d22c1874..8fce5e2f 100644 --- a/src/routes/main/MainStackNavigator.tsx +++ b/src/routes/main/MainStackNavigator.tsx @@ -37,14 +37,14 @@ export type MainStackParams = { screenType: ScreenType; }; CaptionScreen: { - title: string; - image: Image; + title?: string; + image?: Image; screenType: ScreenType; selectedTags?: MomentTagType[]; + moment?: MomentType; }; TagFriendsScreen: { - image: Image; - screenType: ScreenType; + imagePath: string; selectedTags?: MomentTagType[]; }; TagSelectionScreen: { diff --git a/src/screens/moments/TagFriendsScreen.tsx b/src/screens/moments/TagFriendsScreen.tsx index c8bca9f4..570c3776 100644 --- a/src/screens/moments/TagFriendsScreen.tsx +++ b/src/screens/moments/TagFriendsScreen.tsx @@ -30,7 +30,7 @@ interface TagFriendsScreenProps { route: TagFriendsScreenRouteProps; } const TagFriendsScreen: React.FC<TagFriendsScreenProps> = ({route}) => { - const {image, selectedTags} = route.params; + const {imagePath, selectedTags} = route.params; const navigation = useNavigation(); const imageRef = useRef(null); const [tags, setTags] = useState<MomentTagType[]>([]); @@ -85,7 +85,7 @@ const TagFriendsScreen: React.FC<TagFriendsScreenProps> = ({route}) => { <Image ref={imageRef} style={styles.image} - source={{uri: image.path}} + source={{uri: imagePath}} resizeMode={'cover'} /> </TouchableWithoutFeedback> diff --git a/src/screens/profile/CaptionScreen.tsx b/src/screens/profile/CaptionScreen.tsx index 8bffd82b..949b61fd 100644 --- a/src/screens/profile/CaptionScreen.tsx +++ b/src/screens/profile/CaptionScreen.tsx @@ -23,7 +23,7 @@ import TaggLoadingIndicator from '../../components/common/TaggLoadingIndicator'; import {TAGG_LIGHT_BLUE_2} from '../../constants'; import {ERROR_UPLOAD, SUCCESS_PIC_UPLOAD} from '../../constants/strings'; import {MainStackParams} from '../../routes'; -import {postMoment, postMomentTags} from '../../services'; +import {patchMoment, postMoment, postMomentTags} from '../../services'; import { loadUserMoments, updateProfileCompletionStage, @@ -47,14 +47,16 @@ interface CaptionScreenProps { } const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { - const {title, image, screenType, selectedTags} = route.params; + const {title, image, screenType, selectedTags, moment} = route.params; const { user: {userId}, } = useSelector((state: RootState) => state.user); const dispatch = useDispatch(); - const [caption, setCaption] = useState(''); + const [caption, setCaption] = useState(moment ? moment.caption : ''); const [loading, setLoading] = useState(false); - const [tags, setTags] = useState<MomentTagType[]>([]); + const [tags, setTags] = useState<MomentTagType[]>( + selectedTags ? selectedTags : [], + ); const [taggedList, setTaggedList] = useState<string>(''); useEffect(() => { @@ -84,22 +86,32 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { }); }; + const handleFailed = () => { + setLoading(false); + setTimeout(() => { + Alert.alert(moment ? 'Error editing moment' : ERROR_UPLOAD); + }, 500); + }; + const handleSuccess = () => { + setLoading(false); + navigateToProfile(); + setTimeout(() => { + Alert.alert(moment ? 'Successfully edited moment!' : SUCCESS_PIC_UPLOAD); + }, 500); + }; + + const formattedTags = () => { + return tags.map((tag) => ({ + x: Math.floor(tag.x), + y: Math.floor(tag.y), + z: Math.floor(tag.z), + user_id: tag.user.id, + })); + }; + const handleShare = async () => { - const handleFailed = () => { - setLoading(false); - setTimeout(() => { - Alert.alert(ERROR_UPLOAD); - }, 500); - }; - const handleSuccess = () => { - setLoading(false); - navigateToProfile(); - setTimeout(() => { - Alert.alert(SUCCESS_PIC_UPLOAD); - }, 500); - }; setLoading(true); - if (!image.filename) { + if (!image?.filename || !title) { return; } const momentResponse = await postMoment( @@ -115,12 +127,7 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { } const momentTagResponse = await postMomentTags( momentResponse.moment_id, - tags.map((tag) => ({ - x: Math.floor(tag.x), - y: Math.floor(tag.y), - z: Math.floor(tag.z), - user_id: tag.user.id, - })), + formattedTags(), ); if (!momentTagResponse) { handleFailed(); @@ -133,6 +140,23 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { handleSuccess(); }; + const handleDone = async () => { + setLoading(true); + if (moment?.moment_id) { + const success = await patchMoment( + moment.moment_id, + caption, + formattedTags(), + ); + if (success) { + dispatch(loadUserMoments(userId)); + handleSuccess(); + } else { + handleFailed(); + } + } + }; + return ( <SearchBackground> {loading ? <TaggLoadingIndicator fullscreen /> : <Fragment />} @@ -148,17 +172,20 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { onPress={() => navigateToProfile()} /> <Button - title="Share" + title={moment ? 'Done' : 'Share'} titleStyle={styles.shareButtonTitle} buttonStyle={styles.button} - onPress={handleShare} + onPress={moment ? handleDone : handleShare} /> </View> - <CaptionScreenHeader style={styles.header} {...{title: title}} /> + <CaptionScreenHeader + style={styles.header} + {...{title: moment ? moment.moment_category : title}} + /> {/* this is the image we want to center our tags' initial location within */} <Image style={styles.image} - source={{uri: image.path}} + source={{uri: moment ? moment.moment_url : image?.path}} resizeMode={'cover'} /> <MentionInput @@ -172,8 +199,11 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { <TouchableOpacity onPress={() => navigation.navigate('TagFriendsScreen', { - image: image, - screenType: screenType, + imagePath: moment + ? moment.moment_url + : image + ? image.path + : '', selectedTags: tags, }) } diff --git a/src/services/MomentService.ts b/src/services/MomentService.ts index af602dc7..c66d2786 100644 --- a/src/services/MomentService.ts +++ b/src/services/MomentService.ts @@ -54,6 +54,38 @@ export const postMoment = async ( return undefined; }; +export const patchMoment = async ( + momentId: string, + caption: string, + tags: { + x: number; + y: number; + z: number; + user_id: string; + }[], +) => { + try { + const request = new FormData(); + request.append('moment_id', momentId); + request.append('captions', JSON.stringify({[momentId]: caption})); + request.append('tags', JSON.stringify(tags)); + const token = await AsyncStorage.getItem('token'); + let response = await fetch(MOMENTS_ENDPOINT, { + method: 'PATCH', + headers: { + 'Content-Type': 'multipart/form-data', + Authorization: 'Token ' + token, + }, + body: request, + }); + let statusCode = response.status; + return statusCode === 200 || statusCode === 201; + } catch (err) { + console.log(err); + } + return false; +}; + export const loadMoments: ( userId: string, token: string, |