diff options
-rw-r--r-- | src/components/moments/MomentPost.tsx | 352 | ||||
-rw-r--r-- | src/components/profile/MomentMoreInfoDrawer.tsx | 7 | ||||
-rw-r--r-- | src/screens/profile/IndividualMoment.tsx | 55 |
3 files changed, 349 insertions, 65 deletions
diff --git a/src/components/moments/MomentPost.tsx b/src/components/moments/MomentPost.tsx index 60cb16fa..8a3cfacb 100644 --- a/src/components/moments/MomentPost.tsx +++ b/src/components/moments/MomentPost.tsx @@ -1,12 +1,42 @@ -import React, {useEffect, useState} from 'react'; -import {StyleSheet} from 'react-native'; -import {useSelector} from 'react-redux'; -import {MomentPostContent, MomentPostHeader} from '.'; +import {useNavigation} from '@react-navigation/native'; +import React, {useContext, useEffect, useMemo, useRef, useState} from 'react'; +import { + Image, + KeyboardAvoidingView, + Platform, + StatusBar, + StyleSheet, + Text, + TouchableOpacity, + View, +} from 'react-native'; +import Animated, {EasingNode} from 'react-native-reanimated'; +import {useDispatch, useSelector, useStore} from 'react-redux'; +import {headerBarOptions} from '../../routes'; +import {MomentContext} from '../../screens/profile/IndividualMoment'; import {deleteMomentTag, loadMomentTags} from '../../services'; +import {loadUserMoments} from '../../store/actions'; import {RootState} from '../../store/rootReducer'; -import {MomentPostType, MomentTagType, ScreenType} from '../../types'; -import {normalize, SCREEN_HEIGHT} from '../../utils'; - +import { + MomentCommentPreviewType, + MomentPostType, + MomentTagType, + ScreenType, + UserType, +} from '../../types'; +import { + getLoggedInUserAsProfilePreview, + getTimePosted, + HeaderHeight, + navigateToProfile, + normalize, + SCREEN_HEIGHT, + SCREEN_WIDTH, +} from '../../utils'; +import {mentionPartTypes, renderTextWithMentions} from '../../utils/comments'; +import {AddComment} from '../comments'; +import CommentsCount from '../comments/CommentsCount'; +import {MomentMoreInfoDrawer, TaggAvatar} from '../profile'; interface MomentPostProps { moment: MomentPostType; userXId: string | undefined; @@ -24,15 +54,13 @@ const MomentPost: React.FC<MomentPostProps> = ({ (state: RootState) => state.user.user, ); - const { - user: {username}, - } = useSelector((state: RootState) => + const {user} = useSelector((state: RootState) => userXId ? state.userX[screenType][userXId] : state.user, ); const [tags, setTags] = useState<MomentTagType[]>([]); const [momentTagId, setMomentTagId] = useState<string>(''); - const isOwnProfile = username === loggedInUsername; + const isOwnProfile = user.username === loggedInUsername; /* * Load tags on initial render to pass tags data to moment header and content @@ -71,18 +99,201 @@ const MomentPost: React.FC<MomentPostProps> = ({ } }; - return ( - <> - <MomentPostHeader - style={styles.postHeader} + // const [tags, setTags] = useState<MomentTagType[]>(momentTags); + const state: RootState = useStore().getState(); + const navigation = useNavigation(); + const dispatch = useDispatch(); + const imageRef = useRef(null); + const [visible, setVisible] = useState(false); + const [fadeValue, setFadeValue] = useState<Animated.Value<number>>( + new Animated.Value(0), + ); + const [commentCount, setCommentCount] = useState<number>( + moment.comments_count, + ); + const [commentPreview, setCommentPreview] = + useState<MomentCommentPreviewType | null>(moment.comment_preview); + const {keyboardVisible, scrollTo} = useContext(MomentContext); + const [hideText, setHideText] = useState(false); + + const [isFullScreen, setIsFullScreen] = useState<boolean>(false); + const [aspectRatio, setAspectRatio] = useState<number>(1); + + const [drawerVisible, setDrawerVisible] = useState(false); + + useEffect( + () => + navigation.setOptions({ + ...headerBarOptions('white', ''), + headerTitle: () => ( + <View + style={{ + flex: 1, + width: SCREEN_WIDTH * 0.6, + flexDirection: 'row', + justifyContent: 'flex-end', + marginVertical: '2%', + }}> + <View + style={{ + width: '80%', + position: 'absolute', + left: '10%', + right: '10%', + height: normalize(70), + }}> + <Text + style={[ + { + textAlign: 'center', + color: 'white', + fontSize: normalize(18), + fontWeight: '700', + lineHeight: normalize(21.48), + letterSpacing: normalize(1.3), + }, + { + fontSize: + moment.moment_category.length > 18 + ? normalize(14) + : normalize(16), + }, + ]}> + {moment.moment_category} + </Text> + </View> + </View> + ), + }), + [moment.moment_id], + ); + + useEffect(() => { + Image.getSize( + moment.moment_url, + (w, h) => { + const isFS = Math.abs(w / h - 9 / 16) < 0.01; + setAspectRatio(w / h); + setIsFullScreen(isFS); + }, + (err) => console.log(err), + ); + }, []); + + useEffect(() => { + const fade = async () => { + Animated.timing(fadeValue, { + toValue: 1, + duration: 250, + easing: EasingNode.linear, + }).start(); + }; + fade(); + }, [fadeValue]); + + useEffect(() => { + if (!keyboardVisible && hideText) { + setHideText(false); + } + }, [keyboardVisible, hideText]); + + const tagsIcon = useMemo(() => { + tags.length > 0 && ( + <Image + source={require('../../assets/icons/tag_indicate.png')} + style={styles.tagIcon} + /> + ); + }, [tags]); + + const MomentUserPreview = () => ( + <TouchableOpacity + onPress={() => + navigateToProfile(state, dispatch, navigation, screenType, user) + } + style={styles.header}> + <TaggAvatar + style={styles.avatar} userXId={userXId} screenType={screenType} - username={isOwnProfile ? loggedInUsername : username} - momentTagId={momentTagId} - removeTag={removeTag} - moment={moment} - tags={tags} + editable={false} + /> + <Text style={styles.headerText}>{user.username}</Text> + </TouchableOpacity> + ); + + const TagsIcon = () => { + return tags.length === 0 ? ( + <Image + source={require('../../assets/icons/tag_indicate.png')} + style={styles.tagIcon} /> + ) : ( + <React.Fragment /> + ); + }; + + const [verticalOffset, setVerticalOffset] = useState<number>(0); + return ( + <> + <StatusBar barStyle={'light-content'} /> + <View + style={{ + backgroundColor: 'black', + width: SCREEN_WIDTH, + height: SCREEN_HEIGHT, + flexDirection: 'column', + justifyContent: 'center', + }}> + <Image + source={{uri: moment.moment_url}} + style={[ + styles.image, + { + height: isFullScreen ? SCREEN_HEIGHT : SCREEN_WIDTH / aspectRatio, + }, + ]} + resizeMode={'cover'} + /> + <View + style={{ + position: 'absolute', + width: SCREEN_WIDTH, + height: SCREEN_HEIGHT, + flexDirection: 'column', + justifyContent: 'space-between', + paddingBottom: '24%', + }}> + <View + style={{ + paddingTop: HeaderHeight, + alignSelf: 'flex-end', + paddingRight: '8%', + }}> + <MomentMoreInfoDrawer + isOpen={drawerVisible} + setIsOpen={setDrawerVisible} + isOwnProfile={isOwnProfile} + momentTagId={momentTagId} + removeTag={removeTag} + dismissScreenAndUpdate={() => { + dispatch(loadUserMoments(loggedInUserId)); + navigation.goBack(); + }} + screenType={screenType} + moment={moment} + tags={tags} + /> + </View> + <KeyboardAvoidingView + behavior={Platform.OS === 'ios' ? 'padding' : 'height'} + keyboardVerticalOffset={-20}> + <View + style={{ + flexDirection: 'column', + justifyContent: 'flex-end', + }}> + <TagsIcon /> <View style={{ position: 'absolute', @@ -91,6 +302,51 @@ const MomentPost: React.FC<MomentPostProps> = ({ }}> <CommentsCount moment={moment} screenType={screenType} /> </View> + <MomentUserPreview /> + {!hideText && ( + <> + {moment.caption !== '' && + renderTextWithMentions({ + value: moment.caption, + styles: styles.captionText, + partTypes: mentionPartTypes('white'), + onPress: (user: UserType) => + navigateToProfile( + state, + dispatch, + navigation, + screenType, + user, + ), + })} + </> + )} + <View> + <AddComment + placeholderText={'Add a comment here!'} + momentId={moment.moment_id} + callback={(message) => { + setCommentPreview({ + commenter: getLoggedInUserAsProfilePreview(state), + comment: message, + }); + setCommentCount(commentCount + 1); + }} + onFocus={() => { + setHideText(true); + setVerticalOffset(SCREEN_HEIGHT * 0.05); + }} + isKeyboardAvoiding={false} + theme={'dark'} + /> + <Text style={styles.text}> + {getTimePosted(moment.date_created)} + </Text> + </View> + </View> + </KeyboardAvoidingView> + </View> + </View> </> ); }; @@ -98,9 +354,63 @@ const MomentPost: React.FC<MomentPostProps> = ({ const styles = StyleSheet.create({ postHeader: {}, postContent: { - minHeight: SCREEN_HEIGHT * 0.8, + // minHeight: SCREEN_HEIGHT * 0.8, paddingBottom: normalize(20), }, + image: { + width: SCREEN_WIDTH, + marginBottom: '3%', + zIndex: 0, + position: 'absolute', + }, + text: { + marginHorizontal: '5%', + color: 'white', + fontWeight: '500', + textAlign: 'right', + marginTop: 5, + }, + captionText: { + position: 'relative', + marginHorizontal: '5%', + color: '#ffffff', + fontWeight: '500', + fontSize: normalize(13), + lineHeight: normalize(15.51), + letterSpacing: normalize(0.6), + marginBottom: normalize(5), + width: SCREEN_WIDTH * 0.79, + }, + tapTag: { + position: 'absolute', + backgroundColor: 'red', + width: 100, + height: 100, + }, + tagIcon: { + width: normalize(30), + height: normalize(30), + bottom: normalize(20), + left: '5%', + }, + avatar: { + width: 48, + aspectRatio: 1, + borderRadius: 100, + marginLeft: '3%', + }, + headerText: { + fontSize: 15, + fontWeight: 'bold', + color: 'white', + paddingHorizontal: '3%', + flex: 1, + }, + header: { + alignItems: 'center', + flexDirection: 'row', + marginBottom: normalize(15), + }, }); export default MomentPost; diff --git a/src/components/profile/MomentMoreInfoDrawer.tsx b/src/components/profile/MomentMoreInfoDrawer.tsx index a796ffd8..a8adcfda 100644 --- a/src/components/profile/MomentMoreInfoDrawer.tsx +++ b/src/components/profile/MomentMoreInfoDrawer.tsx @@ -169,7 +169,6 @@ const MomentMoreInfoDrawer: React.FC<MomentMoreInfoDrawerProps> = (props) => { return ( <> <TouchableOpacity - style={styles.icon} onPress={() => { setIsOpen(true); }}> @@ -184,10 +183,4 @@ const MomentMoreInfoDrawer: React.FC<MomentMoreInfoDrawerProps> = (props) => { ); }; -const styles = StyleSheet.create({ - icon: { - marginRight: '3%', - }, -}); - export default MomentMoreInfoDrawer; diff --git a/src/screens/profile/IndividualMoment.tsx b/src/screens/profile/IndividualMoment.tsx index f8113aba..4088895a 100644 --- a/src/screens/profile/IndividualMoment.tsx +++ b/src/screens/profile/IndividualMoment.tsx @@ -1,10 +1,9 @@ -import {BlurView} from '@react-native-community/blur'; import {RouteProp} from '@react-navigation/native'; import {StackNavigationProp} from '@react-navigation/stack'; import React, {useEffect, useRef, useState} from 'react'; import {FlatList, Keyboard, StyleSheet} from 'react-native'; import {useSelector} from 'react-redux'; -import {IndividualMomentTitleBar, MomentPost} from '../../components'; +import {MomentPost, TabsGradient} from '../../components'; import {AVATAR_DIM} from '../../constants'; import {MainStackParams} from '../../routes'; import {RootState} from '../../store/rootreducer'; @@ -84,41 +83,23 @@ const IndividualMoment: React.FC<IndividualMomentProps> = ({ keyboardVisible, scrollTo, }}> - <BlurView - blurType="light" - blurAmount={30} - reducedTransparencyFallbackColor="white" - style={styles.contentContainer}> - <IndividualMomentTitleBar - style={styles.header} - close={() => navigation.goBack()} - title={moment_category} - /> - <FlatList - ref={scrollRef} - data={momentData} - contentContainerStyle={styles.listContentContainer} - renderItem={({item, index}) => ( - <MomentPost - moment={item} - userXId={userXId} - screenType={screenType} - index={index} - /> - )} - keyExtractor={(item, _) => item.moment_id} - showsVerticalScrollIndicator={false} - initialScrollIndex={initialIndex} - onScrollToIndexFailed={() => { - // TODO: code below does not work, index resets to 0 - // const wait = new Promise((resolve) => setTimeout(resolve, 500)); - // wait.then(() => { - // console.log('scrolling to ', initialIndex); - // scrollRef.current?.scrollToIndex({index: initialIndex}); - // }); - }} - /> - </BlurView> + <FlatList + ref={scrollRef} + data={momentData} + renderItem={({item, index}) => ( + <MomentPost + moment={item} + userXId={userXId} + screenType={screenType} + index={index} + /> + )} + keyExtractor={(item, _) => item.moment_id} + showsVerticalScrollIndicator={false} + initialScrollIndex={initialIndex} + pagingEnabled + /> + <TabsGradient /> </MomentContext.Provider> ); }; |