diff options
author | Leon Jiang <35908040+leonyjiang@users.noreply.github.com> | 2021-04-03 20:54:09 -0400 |
---|---|---|
committer | Leon Jiang <35908040+leonyjiang@users.noreply.github.com> | 2021-04-03 21:00:20 -0400 |
commit | cce0f8510ac691618c69d76daacac4752800b8c1 (patch) | |
tree | ef5fc41183946c86bbb7548a09354ce1915c4439 /src/components | |
parent | b5401f35d296e21beec41f042d97bffdbdc0c05a (diff) |
Fix profile socials bar animation
Diffstat (limited to 'src/components')
-rw-r--r-- | src/components/profile/Content.tsx | 56 | ||||
-rw-r--r-- | src/components/profile/Cover.tsx | 2 | ||||
-rw-r--r-- | src/components/suggestedPeople/SPTaggsBar.tsx | 133 | ||||
-rw-r--r-- | src/components/suggestedPeople/index.ts | 1 | ||||
-rw-r--r-- | src/components/taggs/TaggPostFooter.tsx | 2 | ||||
-rw-r--r-- | src/components/taggs/TaggsBar.tsx | 93 |
6 files changed, 199 insertions, 88 deletions
diff --git a/src/components/profile/Content.tsx b/src/components/profile/Content.tsx index 9c33eabc..dd68ab17 100644 --- a/src/components/profile/Content.tsx +++ b/src/components/profile/Content.tsx @@ -1,14 +1,10 @@ import React, {useCallback, useEffect, useRef, useState} from 'react'; -import { - LayoutChangeEvent, - NativeScrollEvent, - NativeSyntheticEvent, - RefreshControl, - StyleSheet, -} from 'react-native'; -import Animated from 'react-native-reanimated'; +import {LayoutChangeEvent, RefreshControl, StyleSheet} from 'react-native'; +import Animated, { + useSharedValue, + useAnimatedScrollHandler, +} from 'react-native-reanimated'; import {useDispatch, useSelector, useStore} from 'react-redux'; -import {COVER_HEIGHT} from '../../constants'; import { blockUnblockUser, loadFriendsData, @@ -20,12 +16,11 @@ import { NO_USER, } from '../../store/initialStates'; import {RootState} from '../../store/rootreducer'; -import {ContentProps} from '../../types'; +import {ScreenType} from '../../types'; import { canViewProfile, fetchUserX, getUserAsProfilePreviewType, - SCREEN_HEIGHT, userLogin, } from '../../utils'; import TaggsBar from '../taggs/TaggsBar'; @@ -35,8 +30,13 @@ import ProfileBody from './ProfileBody'; import ProfileCutout from './ProfileCutout'; import ProfileHeader from './ProfileHeader'; import PublicProfile from './PublicProfile'; +import {useScrollToTop} from '@react-navigation/native'; -const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => { +interface ContentProps { + userXId: string | undefined; + screenType: ScreenType; +} +const Content: React.FC<ContentProps> = ({userXId, screenType}) => { const dispatch = useDispatch(); const { user = NO_USER, @@ -60,13 +60,13 @@ const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => { * If scrolling is enabled. Set to false before scrolling up for the tutorial. */ const [scrollEnabled, setScrollEnabled] = useState<boolean>(true); + const y = useSharedValue<number>(0); /** * States */ const [isBlocked, setIsBlocked] = useState<boolean>(false); const [profileBodyHeight, setProfileBodyHeight] = useState(0); - const [shouldBounce, setShouldBounce] = useState<boolean>(true); const [refreshing, setRefreshing] = useState<boolean>(false); const onRefresh = useCallback(() => { @@ -103,45 +103,32 @@ const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => { * updateUserXFriends updates friends list for the user. */ const handleBlockUnblock = async (callback?: () => void) => { - await dispatch( + dispatch( blockUnblockUser( loggedInUser, getUserAsProfilePreviewType(user, profile), isBlocked, ), ); - await dispatch(loadFriendsData(loggedInUser.userId)); - await dispatch(updateUserXFriends(user.userId, state)); + dispatch(loadFriendsData(loggedInUser.userId)); + dispatch(updateUserXFriends(user.userId, state)); if (callback) { callback(); } }; - const handleScroll = (e: NativeSyntheticEvent<NativeScrollEvent>) => { - /** - * Set the new y position - */ - const newY = e.nativeEvent.contentOffset.y; - y.setValue(newY); + const scrollHandler = useAnimatedScrollHandler((event) => { + y.value = event.contentOffset.y; + }); - /** - * 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); - } - }; + useScrollToTop(scrollViewRef); return ( <Animated.ScrollView ref={scrollViewRef} contentContainerStyle={styles.contentContainer} style={styles.container} - onScroll={(e) => handleScroll(e)} - bounces={shouldBounce} + onScroll={scrollHandler} showsVerticalScrollIndicator={false} scrollEventThrottle={1} stickyHeaderIndices={[4]} @@ -165,7 +152,6 @@ const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => { /> <TaggsBar {...{y, profileBodyHeight, userXId, screenType}} - whiteRing={undefined} /> {canViewProfile(state, userXId, screenType) ? ( <PublicProfile diff --git a/src/components/profile/Cover.tsx b/src/components/profile/Cover.tsx index ee804ff3..27777b64 100644 --- a/src/components/profile/Cover.tsx +++ b/src/components/profile/Cover.tsx @@ -27,7 +27,7 @@ const Cover: React.FC<CoverProps> = ({userXId, screenType}) => { const styles = StyleSheet.create({ container: { - position: 'absolute', + ...StyleSheet.absoluteFillObject, }, image: { width: IMAGE_WIDTH, diff --git a/src/components/suggestedPeople/SPTaggsBar.tsx b/src/components/suggestedPeople/SPTaggsBar.tsx new file mode 100644 index 00000000..adac6dcf --- /dev/null +++ b/src/components/suggestedPeople/SPTaggsBar.tsx @@ -0,0 +1,133 @@ +import React, {useEffect, useState} from 'react'; +import {StyleSheet} from 'react-native'; +import Animated from 'react-native-reanimated'; +import {useDispatch, useSelector, useStore} from 'react-redux'; +import {INTEGRATED_SOCIAL_LIST, SOCIAL_LIST} from '../../constants'; +import {getLinkedSocials} from '../../services'; +import {loadIndividualSocial, updateSocial} from '../../store/actions'; +import {RootState} from '../../store/rootReducer'; +import {ScreenType} from '../../types'; +import {canViewProfile} from '../../utils'; +import Tagg from '../taggs/Tagg'; + +const {View, ScrollView} = Animated; +interface TaggsBarProps { + userXId: string | undefined; + screenType: ScreenType; + linkedSocials?: string[]; +} +const TaggsBar: React.FC<TaggsBarProps> = ({ + userXId, + screenType, + linkedSocials, +}) => { + let [taggs, setTaggs] = useState<Object[]>([]); + let [taggsNeedUpdate, setTaggsNeedUpdate] = useState(true); + const {user} = useSelector((state: RootState) => + userXId ? state.userX[screenType][userXId] : state.user, + ); + const state: RootState = useStore().getState(); + const allowTaggsNavigation = canViewProfile(state, userXId, screenType); + + const dispatch = useDispatch(); + + /** + * Updates the individual social that needs update + * If username is empty, update nonintegrated socials like Snapchat and TikTok + * @param socialType Type of the social that needs update + */ + const handleSocialUpdate = (socialType: string, username: string) => { + if (username !== '') { + dispatch(updateSocial(socialType, username)); + } else { + dispatch(loadIndividualSocial(user.userId, socialType)); + } + }; + + /** + * This useEffect should be called evey time the user being viewed is changed OR + * And update is triggered manually + */ + useEffect(() => { + const loadData = async () => { + const socials: string[] = linkedSocials + ? linkedSocials + : await getLinkedSocials(user.userId); + const unlinkedSocials = SOCIAL_LIST.filter( + (s) => socials.indexOf(s) === -1, + ); + let new_taggs = []; + let i = 0; + for (let social of socials) { + new_taggs.push( + <Tagg + key={i} + social={social} + userXId={userXId} + user={user} + isLinked={true} + isIntegrated={INTEGRATED_SOCIAL_LIST.indexOf(social) !== -1} + setTaggsNeedUpdate={setTaggsNeedUpdate} + setSocialDataNeedUpdate={handleSocialUpdate} + whiteRing={true} + allowNavigation={allowTaggsNavigation} + />, + ); + i++; + } + if (!userXId) { + for (let social of unlinkedSocials) { + new_taggs.push( + <Tagg + key={i} + social={social} + isLinked={false} + isIntegrated={INTEGRATED_SOCIAL_LIST.indexOf(social) !== -1} + setTaggsNeedUpdate={setTaggsNeedUpdate} + setSocialDataNeedUpdate={handleSocialUpdate} + userXId={userXId} + user={user} + whiteRing={true} + allowNavigation={allowTaggsNavigation} + />, + ); + i++; + } + } + setTaggs(new_taggs); + setTaggsNeedUpdate(false); + }; + if (user.userId) { + loadData(); + } + }, [taggsNeedUpdate, user]); + + return taggs.length > 0 ? ( + <View style={styles.spContainer}> + <ScrollView + horizontal + showsHorizontalScrollIndicator={false} + contentContainerStyle={styles.contentContainer}> + {taggs} + </ScrollView> + </View> + ) : ( + <></> + ); +}; + +const styles = StyleSheet.create({ + spContainer: { + shadowColor: '#000', + shadowRadius: 10, + shadowOffset: {width: 0, height: 2}, + zIndex: 1, + marginBottom: 25, + }, + contentContainer: { + alignItems: 'center', + paddingBottom: 5, + }, +}); + +export default TaggsBar; diff --git a/src/components/suggestedPeople/index.ts b/src/components/suggestedPeople/index.ts index 515f6fb4..339c9ae0 100644 --- a/src/components/suggestedPeople/index.ts +++ b/src/components/suggestedPeople/index.ts @@ -1,2 +1,3 @@ export {default as MutualFriends} from './MutualFriends'; export {default as BadgesDropdown} from './BadgesDropdown'; +export {default as SPTaggsBar} from './SPTaggsBar'; diff --git a/src/components/taggs/TaggPostFooter.tsx b/src/components/taggs/TaggPostFooter.tsx index ae9d889d..750f1793 100644 --- a/src/components/taggs/TaggPostFooter.tsx +++ b/src/components/taggs/TaggPostFooter.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import {Linking, StyleSheet, View} from 'react-native'; +import {StyleSheet, View} from 'react-native'; import {Text} from 'react-native-animatable'; import {handleOpenSocialUrlOnBrowser} from '../../utils'; import {DateLabel} from '../common'; diff --git a/src/components/taggs/TaggsBar.tsx b/src/components/taggs/TaggsBar.tsx index a87da3e2..87dabc3d 100644 --- a/src/components/taggs/TaggsBar.tsx +++ b/src/components/taggs/TaggsBar.tsx @@ -1,12 +1,16 @@ -import React, {Fragment, useEffect, useState} from 'react'; +import React, {useEffect, useState} from 'react'; import {StyleSheet} from 'react-native'; -import Animated from 'react-native-reanimated'; -import {useSafeAreaInsets} from 'react-native-safe-area-context'; +import Animated, { + useDerivedValue, + interpolate, + Extrapolate, + useAnimatedStyle, +} from 'react-native-reanimated'; import {useDispatch, useSelector, useStore} from 'react-redux'; import { INTEGRATED_SOCIAL_LIST, - PROFILE_CUTOUT_BOTTOM_Y, SOCIAL_LIST, + PROFILE_CUTOUT_BOTTOM_Y, } from '../../constants'; import {getLinkedSocials} from '../../services'; import {loadIndividualSocial, updateSocial} from '../../store/actions'; @@ -14,14 +18,14 @@ import {RootState} from '../../store/rootReducer'; import {ScreenType} from '../../types'; import {canViewProfile} from '../../utils'; import Tagg from './Tagg'; +import {useSafeAreaInsets} from 'react-native-safe-area-context'; -const {View, ScrollView, interpolate, Extrapolate} = Animated; +const {View, ScrollView} = Animated; interface TaggsBarProps { - y: Animated.Value<number>; + y: Animated.SharedValue<number>; profileBodyHeight: number; userXId: string | undefined; screenType: ScreenType; - whiteRing: boolean | undefined; linkedSocials?: string[]; } const TaggsBar: React.FC<TaggsBarProps> = ({ @@ -29,7 +33,6 @@ const TaggsBar: React.FC<TaggsBarProps> = ({ profileBodyHeight, userXId, screenType, - whiteRing, linkedSocials, }) => { let [taggs, setTaggs] = useState<Object[]>([]); @@ -41,7 +44,7 @@ const TaggsBar: React.FC<TaggsBarProps> = ({ const allowTaggsNavigation = canViewProfile(state, userXId, screenType); const dispatch = useDispatch(); - + const insetTop = useSafeAreaInsets().top; /** * Updates the individual social that needs update * If username is empty, update nonintegrated socials like Snapchat and TikTok @@ -80,13 +83,13 @@ const TaggsBar: React.FC<TaggsBarProps> = ({ isIntegrated={INTEGRATED_SOCIAL_LIST.indexOf(social) !== -1} setTaggsNeedUpdate={setTaggsNeedUpdate} setSocialDataNeedUpdate={handleSocialUpdate} - whiteRing={whiteRing ? whiteRing : undefined} + whiteRing={false} allowNavigation={allowTaggsNavigation} />, ); i++; } - if (!userXId && !whiteRing) { + if (!userXId) { for (let social of unlinkedSocials) { new_taggs.push( <Tagg @@ -98,7 +101,7 @@ const TaggsBar: React.FC<TaggsBarProps> = ({ setSocialDataNeedUpdate={handleSocialUpdate} userXId={userXId} user={user} - whiteRing={whiteRing ? whiteRing : undefined} + whiteRing={false} allowNavigation={allowTaggsNavigation} />, ); @@ -112,67 +115,55 @@ const TaggsBar: React.FC<TaggsBarProps> = ({ loadData(); } }, [taggsNeedUpdate, user]); -// TODO: (Leon) use reanimated v2 - const shadowOpacity = 0; - // const shadowOpacity: Animated.Node<number> = interpolate(y, { - // inputRange: [ - // PROFILE_CUTOUT_BOTTOM_Y + profileBodyHeight, - // PROFILE_CUTOUT_BOTTOM_Y + profileBodyHeight + 20, - // ], - // outputRange: [0, 0.2], - // extrapolate: Extrapolate.CLAMP, - // }); - // TODO: (Leon) use reanimated v2 - const paddingTop = 0; - // const paddingTop: Animated.Node<number> = interpolate(y, { - // inputRange: [ - // PROFILE_CUTOUT_BOTTOM_Y + - // profileBodyHeight - - // (useSafeAreaInsets().top + 10), - // PROFILE_CUTOUT_BOTTOM_Y + profileBodyHeight, - // ], - // outputRange: [10, useSafeAreaInsets().top], - // extrapolate: Extrapolate.CLAMP, - // }); + const paddingTopStylesProgress = useDerivedValue(() => + interpolate( + y.value, + [PROFILE_CUTOUT_BOTTOM_Y, PROFILE_CUTOUT_BOTTOM_Y + profileBodyHeight], + [0, 1], + Extrapolate.CLAMP, + ), + ); + const shadowOpacityStylesProgress = useDerivedValue(() => + interpolate( + y.value, + [ + PROFILE_CUTOUT_BOTTOM_Y + profileBodyHeight, + PROFILE_CUTOUT_BOTTOM_Y + profileBodyHeight + insetTop, + ], + [0, 1], + Extrapolate.CLAMP, + ), + ); + const animatedStyles = useAnimatedStyle(() => ({ + shadowOpacity: shadowOpacityStylesProgress.value / 5, + paddingTop: paddingTopStylesProgress.value * insetTop, + })); return taggs.length > 0 ? ( - <View - style={ - whiteRing - ? [styles.spContainer] - : [styles.container, {shadowOpacity, paddingTop}] - }> + <View style={[styles.container, animatedStyles]}> <ScrollView horizontal showsHorizontalScrollIndicator={false} - contentContainerStyle={styles.contentContainer}> + contentContainerStyle={[styles.contentContainer]}> {taggs} </ScrollView> </View> ) : ( - <Fragment /> + <></> ); }; const styles = StyleSheet.create({ - spContainer: { - shadowColor: '#000', - shadowRadius: 10, - shadowOffset: {width: 0, height: 2}, - zIndex: 1, - marginBottom: 25, - }, container: { backgroundColor: 'white', shadowColor: '#000', shadowRadius: 10, shadowOffset: {width: 0, height: 2}, zIndex: 1, - paddingBottom: 5, }, contentContainer: { alignItems: 'center', - paddingBottom: 5, + paddingBottom: 15, }, }); |