import React, {useCallback, useEffect, useState} from 'react'; import { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, RefreshControl, StyleSheet, View, } from 'react-native'; import Animated from 'react-native-reanimated'; import { MomentType, ProfilePreviewType, ProfileType, ScreenType, UserType, } from '../../types'; import {COVER_HEIGHT, defaultMoments} from '../../constants'; import {fetchUserX, SCREEN_HEIGHT, userLogin} from '../../utils'; import TaggsBar from '../taggs/TaggsBar'; import {Moment} from '../moments'; import ProfileBody from './ProfileBody'; import ProfileCutout from './ProfileCutout'; import ProfileHeader from './ProfileHeader'; import {useDispatch, useSelector, useStore} from 'react-redux'; import {RootState} from '../../store/rootreducer'; import { followUnfollowUser, blockUnblockUser, loadFollowData, updateUserXFollowersAndFollowing, } from '../../store/actions'; import { NO_USER, NO_PROFILE, EMPTY_PROFILE_PREVIEW_LIST, EMPTY_MOMENTS_LIST, } from '../../store/initialStates'; import {Cover} from '.'; import {Background} from '../onboarding'; interface ContentProps { y: Animated.Value; userXId: string | undefined; screenType: ScreenType; } const Content: React.FC = ({y, userXId, screenType}) => { const dispatch = useDispatch(); const {user = NO_USER, profile = NO_PROFILE} = userXId ? useSelector((state: RootState) => state.userX[screenType][userXId]) : useSelector((state: RootState) => state.user); const {followers = EMPTY_PROFILE_PREVIEW_LIST} = userXId ? useSelector((state: RootState) => state.userX[screenType][userXId]) : useSelector((state: RootState) => state.follow); const {moments = EMPTY_MOMENTS_LIST} = userXId ? useSelector((state: RootState) => state.userX[screenType][userXId]) : useSelector((state: RootState) => state.moments); const {blockedUsers = EMPTY_PROFILE_PREVIEW_LIST} = useSelector( (state: RootState) => state.blocked, ); const {user: loggedInUser = NO_USER} = useSelector( (state: RootState) => state.user, ); const state = useStore().getState(); /** * States */ const [imagesMap, setImagesMap] = useState>( new Map(), ); const [isFollowed, setIsFollowed] = useState(false); const [isBlocked, setIsBlocked] = useState(false); const [profileBodyHeight, setProfileBodyHeight] = useState(0); const [shouldBounce, setShouldBounce] = useState(true); const [refreshing, setRefreshing] = useState(false); const onRefresh = useCallback(() => { const refrestState = async () => { if (!userXId) { await userLogin(dispatch, loggedInUser); } else { await fetchUserX(dispatch, user, screenType); } }; setRefreshing(true); refrestState().then(() => { setRefreshing(false); }); }, []); /** * If own profile is being viewed then do not show the follow button. */ const isOwnProfile = loggedInUser.username === user.username; const onLayout = (e: LayoutChangeEvent) => { const {height} = e.nativeEvent.layout; setProfileBodyHeight(height); }; const createImagesMap = useCallback(() => { var map = new Map(); moments.forEach(function (imageObject) { var moment_category = imageObject.moment_category; if (map.has(moment_category)) { map.get(moment_category).push(imageObject); } else { map.set(moment_category, [imageObject]); } }); setImagesMap(map); }, [moments]); useEffect(() => { createImagesMap(); }, [createImagesMap]); /** * This hook is called on load of profile and when you update the followers list. */ useEffect(() => { const isActuallyFollowed = followers.some( (follower) => follower.username === loggedInUser.username, ); if (isFollowed != isActuallyFollowed) { setIsFollowed(isActuallyFollowed); } }, [followers]); useEffect(() => { const isActuallyBlocked = blockedUsers.some( (cur_user) => user.username === cur_user.username, ); if (isBlocked != isActuallyBlocked) { setIsBlocked(isActuallyBlocked); } }, [blockedUsers, user]); /** * The object returned by this method is added to the list of blocked / followed users by the reducer. * Which helps us prevent an extra api call to the backend just to fetch a user. */ const getUserAsProfilePreviewType = ( passedInUser: UserType, passedInProfile: ProfileType, ): ProfilePreviewType => { const fullName = passedInProfile.name.split(' '); return { id: passedInUser.userId, username: passedInUser.username, first_name: fullName[0], last_name: fullName[1], }; }; /** * Handles a click on the follow / unfollow button. * followUnfollowUser takes care of updating the following list for loggedInUser * updateUserXFollowersAndFollowing updates followers and following list for the followed user. */ const handleFollowUnfollow = async () => { await dispatch( followUnfollowUser( loggedInUser, getUserAsProfilePreviewType(user, profile), isFollowed, ), ); await dispatch(updateUserXFollowersAndFollowing(user.userId, state)); }; /** * Handles a click on the block / unblock button. * loadFollowData updates followers / following list for the logged in user * updateUserXFollowersAndFollowing updates followers and following list for the followed user. */ const handleBlockUnblock = async () => { await dispatch( blockUnblockUser( loggedInUser, getUserAsProfilePreviewType(user, profile), isBlocked, ), ); await dispatch(loadFollowData(loggedInUser.userId)); await dispatch(updateUserXFollowersAndFollowing(user.userId, state)); }; const handleScroll = (e: NativeSyntheticEvent) => { /** * Set the new y position */ const newY = e.nativeEvent.contentOffset.y; y.setValue(newY); /** * Do not allow overflow of scroll on bottom of the screen * SCREEN_HEIGHT - COVER_HEIGHT = Height of the scroll view */ if (newY >= SCREEN_HEIGHT - COVER_HEIGHT) { setShouldBounce(false); } else if (newY === 0) { setShouldBounce(true); } }; return ( handleScroll(e)} bounces={shouldBounce} showsVerticalScrollIndicator={false} scrollEventThrottle={1} refreshControl={ }> {defaultMoments.map((title, index) => ( ))} ); }; const styles = StyleSheet.create({ refreshControlContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', }, container: { flex: 1, }, momentsContainer: { backgroundColor: '#f2f2f2', paddingBottom: SCREEN_HEIGHT / 10, }, }); export default Content;