From b5ecbf3e421e9e6f1dbab9f3f851d265ae8470c6 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Mon, 14 Dec 2020 19:22:35 -0500 Subject: [TMA-406&201] User Handle UI for Individual Moments (#129) * initial work * made big progress towards flatlist moment view * UI done, just need to pass in data now * minor fixes to get things actually running correctly * vertical scroll working * initial index working * moment drawer text color to red * moved report to drawer * removed garbage * added ? --- src/assets/ionicons/close-outline.svg | 1 + src/components/comments/CommentsCount.tsx | 16 +- src/components/common/GenericMoreInfoDrawer.tsx | 2 +- src/components/moments/CaptionScreenHeader.tsx | 3 +- .../moments/IndividualMomentTitleBar.tsx | 45 ++++ src/components/moments/MomentPostContent.tsx | 85 +++++++ src/components/moments/MomentPostHeader.tsx | 84 +++++++ src/components/moments/index.ts | 5 +- src/components/profile/Avatar.tsx | 5 +- src/components/profile/MomentMoreInfoDrawer.tsx | 61 ++++- src/routes/Routes.tsx | 2 +- src/screens/profile/IndividualMoment.tsx | 253 ++++++--------------- src/screens/profile/MomentCommentsScreen.tsx | 2 +- src/services/MomentServices.ts | 2 +- src/services/ReportingService.ts | 9 +- src/types/types.ts | 2 +- 16 files changed, 366 insertions(+), 211 deletions(-) create mode 100644 src/assets/ionicons/close-outline.svg create mode 100644 src/components/moments/IndividualMomentTitleBar.tsx create mode 100644 src/components/moments/MomentPostContent.tsx create mode 100644 src/components/moments/MomentPostHeader.tsx (limited to 'src') diff --git a/src/assets/ionicons/close-outline.svg b/src/assets/ionicons/close-outline.svg new file mode 100644 index 00000000..4e8abc1c --- /dev/null +++ b/src/assets/ionicons/close-outline.svg @@ -0,0 +1 @@ +Close \ No newline at end of file diff --git a/src/components/comments/CommentsCount.tsx b/src/components/comments/CommentsCount.tsx index d210c39a..325e2788 100644 --- a/src/components/comments/CommentsCount.tsx +++ b/src/components/comments/CommentsCount.tsx @@ -1,8 +1,8 @@ +import {useNavigation} from '@react-navigation/native'; import * as React from 'react'; -import {Text} from 'react-native-animatable'; import {StyleSheet, TouchableOpacity} from 'react-native'; +import {Text} from 'react-native-animatable'; import CommentIcon from '../../assets/icons/moment-comment-icon.svg'; -import {useNavigation} from '@react-navigation/native'; import {ScreenType} from '../../types'; /** @@ -11,20 +11,20 @@ import {ScreenType} from '../../types'; */ type CommentsCountProps = { - comments_count: string; - moment_id: string; + commentsCount: string; + momentId: string; screenType: ScreenType; }; const CommentsCount: React.FC = ({ - comments_count, - moment_id, + commentsCount, + momentId, screenType, }) => { const navigation = useNavigation(); const navigateToCommentsScreen = async () => { navigation.push('MomentCommentsScreen', { - moment_id, + moment_id: momentId, screenType, }); }; @@ -33,7 +33,7 @@ const CommentsCount: React.FC = ({ navigateToCommentsScreen()}> - {comments_count !== '0' ? comments_count : ''} + {commentsCount !== '0' ? commentsCount : ''} diff --git a/src/components/common/GenericMoreInfoDrawer.tsx b/src/components/common/GenericMoreInfoDrawer.tsx index 5c58f903..3607ef8f 100644 --- a/src/components/common/GenericMoreInfoDrawer.tsx +++ b/src/components/common/GenericMoreInfoDrawer.tsx @@ -74,7 +74,7 @@ const styles = StyleSheet.create({ panelButtonTitle: { fontSize: 18, fontWeight: 'bold', - color: 'black', + color: 'red', }, icon: { height: 25, diff --git a/src/components/moments/CaptionScreenHeader.tsx b/src/components/moments/CaptionScreenHeader.tsx index 4715b4ef..46dfddfe 100644 --- a/src/components/moments/CaptionScreenHeader.tsx +++ b/src/components/moments/CaptionScreenHeader.tsx @@ -20,7 +20,8 @@ const styles = StyleSheet.create({ container: { flexDirection: 'row', justifyContent: 'center', - height: 30, + alignItems: 'center', + height: '5%', }, headerContainer: { position: 'absolute', diff --git a/src/components/moments/IndividualMomentTitleBar.tsx b/src/components/moments/IndividualMomentTitleBar.tsx new file mode 100644 index 00000000..bd5b307f --- /dev/null +++ b/src/components/moments/IndividualMomentTitleBar.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import {TouchableOpacity} from 'react-native'; +import {Text, View, StyleSheet, ViewProps} from 'react-native'; +import CloseIcon from '../../assets/ionicons/close-outline.svg'; + +interface IndividualMomentTitleBarProps extends ViewProps { + title: string; + close: () => void; +} +const IndividualMomentTitleBar: React.FC = ({ + title, + close, + style, +}) => { + return ( + + + + + {title} + + ); +}; + +const styles = StyleSheet.create({ + container: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + height: '5%', + }, + header: { + fontSize: 20, + fontWeight: 'bold', + color: 'white', + }, + closeButton: { + position: 'absolute', + height: '50%', + aspectRatio: 1, + left: '3%', + }, +}); + +export default IndividualMomentTitleBar; diff --git a/src/components/moments/MomentPostContent.tsx b/src/components/moments/MomentPostContent.tsx new file mode 100644 index 00000000..93271fa1 --- /dev/null +++ b/src/components/moments/MomentPostContent.tsx @@ -0,0 +1,85 @@ +import React, {useEffect} from 'react'; +import {Image, StyleSheet, Text, View, ViewProps} from 'react-native'; +import {getMomentCommentsCount} from '../../services'; +import {ScreenType} from '../../types'; +import {getTimePosted, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; +import {CommentsCount} from '../comments'; + +interface MomentPostContentProps extends ViewProps { + screenType: ScreenType; + momentId: string; + caption: string; + pathHash: string; + dateTime: string; +} +const MomentPostContent: React.FC = ({ + screenType, + momentId, + caption, + pathHash, + dateTime, + style, +}) => { + const [elapsedTime, setElapsedTime] = React.useState(); + const [comments_count, setCommentsCount] = React.useState(''); + + useEffect(() => { + setElapsedTime(getTimePosted(dateTime)); + getMomentCommentsCount(momentId, setCommentsCount); + }, [dateTime, momentId]); + return ( + + + + + {elapsedTime} + + {caption} + + ); +}; + +const styles = StyleSheet.create({ + container: { + height: SCREEN_HEIGHT, + }, + image: { + width: SCREEN_WIDTH, + aspectRatio: 1, + marginBottom: '3%', + }, + footerContainer: { + flexDirection: 'row', + justifyContent: 'space-between', + marginLeft: '7%', + marginRight: '5%', + marginBottom: '2%', + }, + text: { + position: 'relative', + paddingBottom: '1%', + paddingTop: '1%', + marginLeft: '7%', + marginRight: '2%', + color: '#ffffff', + fontWeight: 'bold', + }, + captionText: { + position: 'relative', + paddingBottom: '34%', + paddingTop: '1%', + marginLeft: '5%', + marginRight: '5%', + color: '#ffffff', + fontWeight: 'bold', + }, +}); +export default MomentPostContent; diff --git a/src/components/moments/MomentPostHeader.tsx b/src/components/moments/MomentPostHeader.tsx new file mode 100644 index 00000000..810ccea9 --- /dev/null +++ b/src/components/moments/MomentPostHeader.tsx @@ -0,0 +1,84 @@ +import React, {useState} from 'react'; +import {StyleSheet, Text, View, ViewProps} from 'react-native'; +import {MomentMoreInfoDrawer} from '..'; +import {loadUserMoments} from '../../store/actions'; +import {useDispatch, useSelector} from 'react-redux'; +import {ScreenType} from '../../types'; +import Avatar from '../profile/Avatar'; +import {useNavigation} from '@react-navigation/native'; +import {RootState} from '../../store/rootReducer'; + +interface MomentPostHeaderProps extends ViewProps { + userXId?: string; + screenType: ScreenType; + username: string; + momentId: string; +} + +const MomentPostHeader: React.FC = ({ + userXId, + screenType, + username, + momentId, + style, +}) => { + const [drawerVisible, setDrawerVisible] = useState(false); + const dispatch = useDispatch(); + const navigation = useNavigation(); + const {userId: loggedInUserId, username: loggedInUserName} = useSelector( + (state: RootState) => state.user.user, + ); + const isOwnProfile = loggedInUserName === username; + + return ( + + + + {username} + + { + dispatch(loadUserMoments(loggedInUserId)); + navigation.pop(); + }} + /> + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: 'space-around', + flexDirection: 'row', + alignItems: 'center', + marginVertical: '2%', + }, + header: { + alignItems: 'center', + flexDirection: 'row', + flex: 1, + }, + avatar: { + flex: 0.2, + aspectRatio: 1, + borderRadius: 999999, + marginLeft: '3%', + }, + headerText: { + fontSize: 15, + fontWeight: 'bold', + color: 'white', + paddingHorizontal: '3%', + flex: 1, + }, +}); +export default MomentPostHeader; diff --git a/src/components/moments/index.ts b/src/components/moments/index.ts index 339e0e19..89fd689c 100644 --- a/src/components/moments/index.ts +++ b/src/components/moments/index.ts @@ -1,2 +1,5 @@ -export {default as CaptionScreenHeader} from '../moments/CaptionScreenHeader'; +export {default as IndividualMomentTitleBar} from './IndividualMomentTitleBar'; +export {default as CaptionScreenHeader} from './CaptionScreenHeader'; +export {default as MomentPostHeader} from './MomentPostHeader'; +export {default as MomentPostContent} from './MomentPostContent'; export {default as Moment} from './Moment'; diff --git a/src/components/profile/Avatar.tsx b/src/components/profile/Avatar.tsx index d3c53043..ba4ec36c 100644 --- a/src/components/profile/Avatar.tsx +++ b/src/components/profile/Avatar.tsx @@ -1,12 +1,13 @@ -import React, {useContext} from 'react'; +import React from 'react'; import {Image, StyleSheet} from 'react-native'; import {useSelector} from 'react-redux'; import {RootState} from '../../store/rootreducer'; import {ScreenType} from '../../types'; const PROFILE_DIM = 100; + interface AvatarProps { - style: object; + style?: object; userXId: string | undefined; screenType: ScreenType; } diff --git a/src/components/profile/MomentMoreInfoDrawer.tsx b/src/components/profile/MomentMoreInfoDrawer.tsx index 18462cbb..91fb3d2b 100644 --- a/src/components/profile/MomentMoreInfoDrawer.tsx +++ b/src/components/profile/MomentMoreInfoDrawer.tsx @@ -1,18 +1,19 @@ import React from 'react'; -import {Alert, TouchableOpacity} from 'react-native'; +import {Alert, StyleSheet, TouchableOpacity, ViewProps} from 'react-native'; import MoreIcon from '../../assets/icons/more_horiz-24px.svg'; -import {deleteMoment} from '../../services'; +import {deleteMoment, sendReport} from '../../services'; import {GenericMoreInfoDrawer} from '../common'; -interface MomentMoreInfoDrawerProps { +interface MomentMoreInfoDrawerProps extends ViewProps { isOpen: boolean; setIsOpen: (visible: boolean) => void; momentId: string; - dismissScreenAndUpdate: Function; + isOwnProfile: boolean; + dismissScreenAndUpdate: () => void; } const MomentMoreInfoDrawer: React.FC = (props) => { - const {momentId, setIsOpen, dismissScreenAndUpdate} = props; + const {momentId, setIsOpen, isOwnProfile, dismissScreenAndUpdate} = props; const handleDeleteMoment = async () => { setIsOpen(false); @@ -38,21 +39,61 @@ const MomentMoreInfoDrawer: React.FC = (props) => { }); }; + const handleReportMoment = async () => { + setIsOpen(false); + setTimeout(() => { + Alert.alert( + 'Report Issue', + undefined, + [ + { + text: 'Mark as inappropriate', + onPress: () => sendReport(momentId, 'Mark as inappropriate'), + }, + { + text: 'Cancel', + style: 'cancel', + }, + { + text: 'Mark as abusive', + onPress: () => sendReport(momentId, 'Mark as abusive'), + }, + ], + {cancelable: false}, + ); + }, 500); + }; + return ( <> { setIsOpen(true); }}> - + {isOwnProfile ? ( + + ) : ( + + )} ); }; +const styles = StyleSheet.create({ + icon: { + marginRight: '3%', + }, +}); + export default MomentMoreInfoDrawer; diff --git a/src/routes/Routes.tsx b/src/routes/Routes.tsx index cb45ead6..38a987f7 100644 --- a/src/routes/Routes.tsx +++ b/src/routes/Routes.tsx @@ -30,7 +30,7 @@ const Routes: React.FC = () => { } else { SplashScreen.hide(); } - }, [userId, userLogin]); + }, [dispatch, userId]); return userId ? : ; }; diff --git a/src/screens/profile/IndividualMoment.tsx b/src/screens/profile/IndividualMoment.tsx index d1b21d0f..469c648e 100644 --- a/src/screens/profile/IndividualMoment.tsx +++ b/src/screens/profile/IndividualMoment.tsx @@ -1,30 +1,18 @@ -import AsyncStorage from '@react-native-community/async-storage'; import {BlurView} from '@react-native-community/blur'; import {RouteProp} from '@react-navigation/native'; import {StackNavigationProp} from '@react-navigation/stack'; -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 {useDispatch, useSelector} from 'react-redux'; +import React from 'react'; +import {FlatList, StyleSheet, View} from 'react-native'; +import {useSelector} from 'react-redux'; import {ProfileStackParams} from 'src/routes/profile/ProfileStack'; import { - CaptionScreenHeader, - CommentsCount, - MomentMoreInfoDrawer, + IndividualMomentTitleBar, + MomentPostContent, + MomentPostHeader, } from '../../components'; -import {getMomentCommentsCount} from '../../services'; -import {sendReport} from '../../services/ReportingService'; -import {loadUserMoments, logout} from '../../store/actions'; -import {DUMMY_USERNAME} from '../../store/initialStates'; import {RootState} from '../../store/rootreducer'; -import { - getTimePosted, - SCREEN_HEIGHT, - SCREEN_WIDTH, - StatusBarHeight, -} from '../../utils'; +import {MomentType} from '../../types'; +import {SCREEN_HEIGHT, SCREEN_WIDTH, StatusBarHeight} from '../../utils'; /** * Individual moment view opened when user clicks on a moment tile @@ -42,89 +30,52 @@ interface IndividualMomentProps { navigation: IndividualMomentNavigationProp; } +const ITEM_HEIGHT = SCREEN_HEIGHT * (9 / 10); + const IndividualMoment: React.FC = ({ route, navigation, }) => { - const { - moment_category, - path_hash, - date_time, - moment_id, - } = route.params.moment; + const {moment_category, moment_id} = route.params.moment; const {userXId, screenType} = route.params; - - const { - user: {userId: loggedInUserId, username: loggedInUsername}, - } = useSelector((state: RootState) => state.user); - + const {username: loggedInUsername} = useSelector( + (state: RootState) => state.user.user, + ); const { user: {username}, } = userXId ? useSelector((state: RootState) => state.userX[screenType][userXId]) - : {user: {username: DUMMY_USERNAME}}; + : useSelector((state: RootState) => state.user); - 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 [drawerVisible, setDrawerVisible] = useState(false); - - useEffect(() => { - const timePeriod = async () => { - setElapsedTime(getTimePosted(date_time)); - }; - - const loadComments = async () => { - const token = await AsyncStorage.getItem('token'); - if (!token) { - dispatch(logout()); - return; - } - getMomentCommentsCount(moment_id, setCommentsCount, token); - }; + const {moments} = userXId + ? useSelector((state: RootState) => state.userX[screenType][userXId]) + : useSelector((state: RootState) => state.moments); - timePeriod(); - loadComments(); - }, [date_time, dispatch, loggedInUserId, moment_id]); + const isOwnProfile = username === loggedInUsername; + const momentData = moments.filter( + (m) => m.moment_category === moment_category, + ); + const initialIndex = momentData.findIndex((m) => m.moment_id === moment_id); - const sendReportAlert = async () => { - const token = await AsyncStorage.getItem('token'); - setIsReporting(true); - Alert.alert( - 'Report Issue', - undefined, - [ - { - text: 'Mark as inappropriate', - onPress: () => - sendReport( - moment_id, - 'Mark as inappropriate', - token ? token : '', - setIsReporting, - ), - }, - { - text: 'Cancel', - onPress: () => setIsReporting(false), - style: 'cancel', - }, - { - text: 'Mark as abusive', - onPress: () => - sendReport( - moment_id, - 'Mark as abusive', - token ? token : '', - setIsReporting, - ), - }, - ], - {cancelable: false}, - ); - }; + const renderMomentPost = ({item}: {item: MomentType}) => ( + + + + + ); return ( = ({ blurAmount={10} reducedTransparencyFallbackColor="white" style={styles.contentContainer}> - -