aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLeon Jiang <35908040+leonyjiang@users.noreply.github.com>2021-04-03 20:54:09 -0400
committerLeon Jiang <35908040+leonyjiang@users.noreply.github.com>2021-04-03 21:00:20 -0400
commitcce0f8510ac691618c69d76daacac4752800b8c1 (patch)
treeef5fc41183946c86bbb7548a09354ce1915c4439 /src
parentb5401f35d296e21beec41f042d97bffdbdc0c05a (diff)
Fix profile socials bar animation
Diffstat (limited to 'src')
-rw-r--r--src/components/profile/Content.tsx56
-rw-r--r--src/components/profile/Cover.tsx2
-rw-r--r--src/components/suggestedPeople/SPTaggsBar.tsx133
-rw-r--r--src/components/suggestedPeople/index.ts1
-rw-r--r--src/components/taggs/TaggPostFooter.tsx2
-rw-r--r--src/components/taggs/TaggsBar.tsx93
-rw-r--r--src/screens/profile/ProfileScreen.tsx12
-rw-r--r--src/screens/suggestedPeople/SPBody.tsx20
-rw-r--r--src/screens/suggestedPeople/SuggestedPeopleScreen.tsx2
-rw-r--r--src/types/types.ts9
10 files changed, 207 insertions, 123 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,
},
});
diff --git a/src/screens/profile/ProfileScreen.tsx b/src/screens/profile/ProfileScreen.tsx
index 313e2f2c..a5d1e495 100644
--- a/src/screens/profile/ProfileScreen.tsx
+++ b/src/screens/profile/ProfileScreen.tsx
@@ -1,12 +1,8 @@
import React from 'react';
import {StatusBar} from 'react-native';
-import Animated from 'react-native-reanimated';
-import {Content, Cover, TabsGradient} from '../../components';
-import {RouteProp, useFocusEffect} from '@react-navigation/native';
+import {Content, TabsGradient} from '../../components';
+import {RouteProp} from '@react-navigation/native';
import {MainStackParams} from '../../routes/';
-import {resetScreenType} from '../../store/actions';
-import {useDispatch, useStore} from 'react-redux';
-import {DUMMY_USERID} from '../../store/initialStates';
/**r
* Profile Screen for a user's profile
@@ -22,8 +18,6 @@ interface ProfileOnboardingProps {
const ProfileScreen: React.FC<ProfileOnboardingProps> = ({route}) => {
const {screenType} = route.params;
let {userXId} = route.params;
- const y = Animated.useValue(0);
- const dispatch = useDispatch();
/**
* This is a double safety check to avoid app crash.
@@ -47,7 +41,7 @@ const ProfileScreen: React.FC<ProfileOnboardingProps> = ({route}) => {
return (
<>
<StatusBar barStyle="dark-content" />
- <Content {...{y, userXId, screenType}} />
+ <Content {...{userXId, screenType}} />
<TabsGradient />
</>
);
diff --git a/src/screens/suggestedPeople/SPBody.tsx b/src/screens/suggestedPeople/SPBody.tsx
index 824f8b1c..fa69d812 100644
--- a/src/screens/suggestedPeople/SPBody.tsx
+++ b/src/screens/suggestedPeople/SPBody.tsx
@@ -3,26 +3,18 @@ import React, {Fragment, useEffect, useMemo, useState} from 'react';
import {StyleSheet, Text, View} from 'react-native';
import {Image} from 'react-native-animatable';
import {TouchableOpacity} from 'react-native-gesture-handler';
-import Animated from 'react-native-reanimated';
-import {useStore} from 'react-redux';
import RequestedButton from '../../assets/ionicons/requested-button.svg';
-import {TaggsBar} from '../../components';
+import {SPTaggsBar} from '../../components';
import {BadgesDropdown, MutualFriends} from '../../components/suggestedPeople';
import {BADGE_DATA} from '../../constants/badges';
-import {RootState} from '../../store/rootReducer';
import {
ProfilePreviewType,
ScreenType,
SuggestedPeopleDataType,
UniversityBadge,
} from '../../types';
-import {
- canViewProfile,
- isIPhoneX,
- normalize,
- SCREEN_HEIGHT,
- SCREEN_WIDTH,
-} from '../../utils';
+import {isIPhoneX, normalize, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
+import {useSharedValue} from 'react-native-reanimated';
interface SPBodyProps {
item: SuggestedPeopleDataType;
@@ -56,7 +48,6 @@ const SPBody: React.FC<SPBodyProps> = ({
}[]
>([]);
const navigation = useNavigation();
- const state: RootState = useStore().getState();
useEffect(() => {
const newBadges: {badge: UniversityBadge; img: any}[] = [];
const findBadgeIcons = (badge: UniversityBadge) => {
@@ -159,12 +150,9 @@ const SPBody: React.FC<SPBodyProps> = ({
{user.id !== loggedInUserId && <FriendButton />}
</View>
</View>
- <TaggsBar
- y={Animated.useValue(0)}
+ <SPTaggsBar
userXId={user.id === loggedInUserId ? undefined : user.id}
- profileBodyHeight={0}
screenType={screenType}
- whiteRing={true}
linkedSocials={social_links}
/>
<View style={styles.marginManager}>
diff --git a/src/screens/suggestedPeople/SuggestedPeopleScreen.tsx b/src/screens/suggestedPeople/SuggestedPeopleScreen.tsx
index a296351f..d6812f41 100644
--- a/src/screens/suggestedPeople/SuggestedPeopleScreen.tsx
+++ b/src/screens/suggestedPeople/SuggestedPeopleScreen.tsx
@@ -226,7 +226,7 @@ const SuggestedPeopleScreen: React.FC = () => {
/>
);
}}
- keyExtractor={(item, index) => index.toString()}
+ keyExtractor={(_, index) => index.toString()}
showsVerticalScrollIndicator={false}
onViewableItemsChanged={onViewableItemsChanged}
onEndReached={() => setPage(page + 1)}
diff --git a/src/types/types.ts b/src/types/types.ts
index b5dc6373..40f2a614 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -1,4 +1,3 @@
-import Animated from 'react-native-reanimated';
import {Channel as ChannelType, StreamChat} from 'stream-chat';
export interface UserType {
@@ -210,14 +209,6 @@ export interface CommentNotificationType {
export interface ThreadNotificationType extends CommentNotificationType {
parent_comment: string;
}
-export interface ContentProps {
- y: Animated.Value<number>;
- userXId: string | undefined;
- screenType: ScreenType;
- setScrollEnabled: (enabled: boolean) => void;
- profileBodyHeight: number;
- scrollViewRef: React.MutableRefObject<null>;
-}
export type NotificationType = {
actor: ProfilePreviewType;