aboutsummaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
authorIvan Chen <ivan@tagg.id>2021-04-09 17:15:29 -0400
committerIvan Chen <ivan@tagg.id>2021-04-09 17:15:29 -0400
commit347e9e450268e4897b8dd241721b84945d9e2ec9 (patch)
tree58334be3724398c886365e99901e4442f5657172 /src/components
parent097b515066f1a0c38cb7fb69cf78b16b945594e5 (diff)
parent3ec56863bfdd47b2ee8d0f0fe5a45be779508660 (diff)
Merge branch 'master' into tma756-bugfix-onpress-tagg-on-sp
# Conflicts: # src/components/taggs/TaggsBar.tsx
Diffstat (limited to 'src/components')
-rw-r--r--src/components/common/BottomDrawer.tsx18
-rw-r--r--src/components/common/GradientBorderButton.tsx2
-rw-r--r--src/components/common/NavigationIcon.tsx10
-rw-r--r--src/components/common/TabsGradient.tsx2
-rw-r--r--src/components/common/TaggPrompt.tsx2
-rw-r--r--src/components/index.ts1
-rw-r--r--src/components/messages/ChannelPreview.tsx131
-rw-r--r--src/components/messages/ChatHeader.tsx84
-rw-r--r--src/components/messages/MessagesHeader.tsx59
-rw-r--r--src/components/messages/index.ts2
-rw-r--r--src/components/profile/Content.tsx63
-rw-r--r--src/components/profile/Cover.tsx2
-rw-r--r--src/components/profile/Friends.tsx8
-rw-r--r--src/components/profile/PublicProfile.tsx5
-rw-r--r--src/components/search/RecentSearches.tsx31
-rw-r--r--src/components/search/SearchBar.tsx33
-rw-r--r--src/components/search/SearchCategories.tsx68
-rw-r--r--src/components/search/SearchResultList.tsx85
-rw-r--r--src/components/search/SearchResults.tsx14
-rw-r--r--src/components/search/SearchResultsBackground.tsx65
-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.tsx90
24 files changed, 663 insertions, 248 deletions
diff --git a/src/components/common/BottomDrawer.tsx b/src/components/common/BottomDrawer.tsx
index bef9434a..988c1e79 100644
--- a/src/components/common/BottomDrawer.tsx
+++ b/src/components/common/BottomDrawer.tsx
@@ -6,7 +6,7 @@ import {
View,
ViewProps,
} from 'react-native';
-import Animated from 'react-native-reanimated';
+import Animated, {interpolateColors} from 'react-native-reanimated';
import BottomSheet from 'reanimated-bottom-sheet';
import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
@@ -50,6 +50,10 @@ const BottomDrawer: React.FC<BottomDrawerProps> = (props) => {
);
};
+ const backgroundColor = interpolateColors(bgAlpha, {
+ inputRange: [0, 1],
+ outputColorRange: ['rgba(0,0,0,0.3)', 'rgba(0,0,0,0)'],
+ });
return (
<Modal
transparent
@@ -75,17 +79,7 @@ const BottomDrawer: React.FC<BottomDrawerProps> = (props) => {
onPress={() => {
setIsOpen(false);
}}>
- <Animated.View
- style={[
- styles.backgroundView,
- {
- backgroundColor: Animated.interpolateColors(bgAlpha, {
- inputRange: [0, 1],
- outputColorRange: ['rgba(0,0,0,0.3)', 'rgba(0,0,0,0)'],
- }),
- },
- ]}
- />
+ <Animated.View style={[styles.backgroundView, {backgroundColor}]} />
</TouchableWithoutFeedback>
</Modal>
);
diff --git a/src/components/common/GradientBorderButton.tsx b/src/components/common/GradientBorderButton.tsx
index 32ac5c52..a5dbde9d 100644
--- a/src/components/common/GradientBorderButton.tsx
+++ b/src/components/common/GradientBorderButton.tsx
@@ -42,7 +42,7 @@ const GradientBorderButton: React.FC<GradientBorderButtonProps> = ({
};
const styles = StyleSheet.create({
container: {
- marginVertical: 15,
+ marginVertical: 10,
},
gradientContainer: {
width: SCREEN_WIDTH / 2 - 40,
diff --git a/src/components/common/NavigationIcon.tsx b/src/components/common/NavigationIcon.tsx
index 1a9934f2..5128f3da 100644
--- a/src/components/common/NavigationIcon.tsx
+++ b/src/components/common/NavigationIcon.tsx
@@ -14,7 +14,8 @@ interface NavigationIconProps extends TouchableOpacityProps {
| 'Upload'
| 'Notifications'
| 'Profile'
- | 'SuggestedPeople';
+ | 'SuggestedPeople'
+ | 'Chat';
disabled?: boolean;
newIcon?: boolean;
}
@@ -44,6 +45,13 @@ const NavigationIcon = (props: NavigationIconProps) => {
: require('../../assets/navigationIcons/notifications.png')
: require('../../assets/navigationIcons/notifications-clicked.png');
break;
+ case 'Chat':
+ imgSrc = props.disabled
+ ? props.newIcon
+ ? require('../../assets/navigationIcons/chat-notifications.png')
+ : require('../../assets/navigationIcons/chat.png')
+ : require('../../assets/navigationIcons/chat-clicked.png');
+ break;
case 'Profile':
imgSrc = props.disabled
? require('../../assets/navigationIcons/profile.png')
diff --git a/src/components/common/TabsGradient.tsx b/src/components/common/TabsGradient.tsx
index a95e8bc3..07c55042 100644
--- a/src/components/common/TabsGradient.tsx
+++ b/src/components/common/TabsGradient.tsx
@@ -14,7 +14,7 @@ const TabsGradient: React.FC = () => {
};
const styles = StyleSheet.create({
gradient: {
- position: 'absolute',
+ ...StyleSheet.absoluteFillObject,
top: (SCREEN_HEIGHT / 10) * 9,
height: SCREEN_HEIGHT / 10,
width: SCREEN_WIDTH,
diff --git a/src/components/common/TaggPrompt.tsx b/src/components/common/TaggPrompt.tsx
index 721b1eb8..5e125d00 100644
--- a/src/components/common/TaggPrompt.tsx
+++ b/src/components/common/TaggPrompt.tsx
@@ -68,7 +68,7 @@ const styles = StyleSheet.create({
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white',
- height: isIPhoneX() ? SCREEN_HEIGHT / 6 : SCREEN_HEIGHT / 5,
+ height: SCREEN_HEIGHT / 4,
},
closeButton: {
position: 'relative',
diff --git a/src/components/index.ts b/src/components/index.ts
index d5649323..47dc583b 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -6,3 +6,4 @@ export * from './taggs';
export * from './comments';
export * from './moments';
export * from './suggestedPeople';
+export * from './messages';
diff --git a/src/components/messages/ChannelPreview.tsx b/src/components/messages/ChannelPreview.tsx
new file mode 100644
index 00000000..312f879a
--- /dev/null
+++ b/src/components/messages/ChannelPreview.tsx
@@ -0,0 +1,131 @@
+import {useNavigation} from '@react-navigation/core';
+import React, {useContext} from 'react';
+import {Image, StyleSheet, Text, View} from 'react-native';
+import {TouchableOpacity} from 'react-native-gesture-handler';
+import {useStore} from 'react-redux';
+import {usernameRegex} from 'src/constants';
+import {ChannelPreviewMessengerProps} from 'stream-chat-react-native';
+import {ChatContext} from '../../App';
+import {
+ LocalAttachmentType,
+ LocalChannelType,
+ LocalCommandType,
+ LocalEventType,
+ LocalMessageType,
+ LocalReactionType,
+ LocalUserType,
+} from '../../types';
+import {normalize, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
+import {getMember, isOnline} from '../../utils/messages';
+
+const ChannelPreview: React.FC<ChannelPreviewMessengerProps<
+ LocalAttachmentType,
+ LocalChannelType,
+ LocalCommandType,
+ LocalEventType,
+ LocalMessageType,
+ LocalReactionType,
+ LocalUserType
+>> = (props) => {
+ const {setChannel} = useContext(ChatContext);
+ const state = useStore().getState();
+ const navigation = useNavigation();
+ const {channel} = props;
+ const member = getMember(channel, state);
+ const online = isOnline(member?.user?.last_active);
+ const unread = channel.state.unreadCount > 0;
+
+ return (
+ <TouchableOpacity
+ style={styles.container}
+ onPress={() => {
+ setChannel(channel);
+ navigation.navigate('Chat');
+ }}>
+ <View>
+ <Image
+ style={styles.avatar}
+ source={
+ member
+ ? {uri: member.user?.thumbnail_url}
+ : require('../../assets/images/avatar-placeholder.png')
+ }
+ />
+ {online && <View style={styles.online} />}
+ </View>
+ <View style={styles.content}>
+ <Text
+ style={[styles.name, unread ? styles.unread : {}]}
+ numberOfLines={1}>
+ {member?.user?.first_name} {member?.user?.last_name}
+ </Text>
+ <Text
+ style={[styles.lastMessage, unread ? styles.unread : {}]}
+ numberOfLines={1}>
+ {channel.state.messages.length > 0
+ ? channel.state.messages[channel.state.messages.length - 1].text
+ : ''}
+ </Text>
+ </View>
+ {unread && <View style={styles.purpleDot} />}
+ </TouchableOpacity>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ flexDirection: 'row',
+ height: Math.round(SCREEN_HEIGHT / 9),
+ width: Math.round(SCREEN_WIDTH * 0.85),
+ alignSelf: 'center',
+ alignItems: 'center',
+ },
+ avatar: {
+ width: normalize(60),
+ height: normalize(60),
+ borderRadius: normalize(62) / 2,
+ },
+ online: {
+ position: 'absolute',
+ backgroundColor: '#6EE7E7',
+ width: normalize(18),
+ height: normalize(18),
+ borderRadius: normalize(18) / 2,
+ borderColor: 'white',
+ borderWidth: 2,
+ bottom: 0,
+ right: 0,
+ },
+ content: {
+ flex: 1,
+ height: '60%',
+ flexDirection: 'column',
+ marginLeft: '5%',
+ },
+ name: {
+ fontWeight: '500',
+ fontSize: normalize(14),
+ lineHeight: normalize(17),
+ },
+ lastMessage: {
+ color: '#828282',
+ fontWeight: '500',
+ fontSize: normalize(12),
+ lineHeight: normalize(14),
+ paddingTop: '5%',
+ },
+ unread: {
+ fontWeight: '700',
+ color: 'black',
+ },
+ purpleDot: {
+ backgroundColor: '#8F01FF',
+ width: normalize(10),
+ height: normalize(10),
+ borderRadius: normalize(10) / 2,
+ marginLeft: '5%',
+ },
+});
+
+export default ChannelPreview;
diff --git a/src/components/messages/ChatHeader.tsx b/src/components/messages/ChatHeader.tsx
new file mode 100644
index 00000000..2bc096ec
--- /dev/null
+++ b/src/components/messages/ChatHeader.tsx
@@ -0,0 +1,84 @@
+import React, {useContext} from 'react';
+import {Image, StyleSheet, View} from 'react-native';
+import {Text} from 'react-native-animatable';
+import {useStore} from 'react-redux';
+import {ChatContext} from '../../App';
+import {ChatHeaderHeight, normalize, StatusBarHeight} from '../../utils';
+import {formatLastSeenText, getMember, isOnline} from '../../utils/messages';
+
+type ChatHeaderProps = {};
+
+const ChatHeader: React.FC<ChatHeaderProps> = () => {
+ const {channel} = useContext(ChatContext);
+ const state = useStore().getState();
+ const member = getMember(channel, state);
+ const online = isOnline(member?.user?.last_active);
+ const lastSeen = formatLastSeenText(member?.user?.last_active);
+
+ return (
+ <View style={styles.container}>
+ <View>
+ <Image
+ style={styles.avatar}
+ source={
+ member
+ ? {uri: member.user?.thumbnail_url}
+ : require('../../assets/images/avatar-placeholder.png')
+ }
+ />
+ {online && <View style={styles.online} />}
+ </View>
+ <View style={styles.content}>
+ <Text style={styles.name} numberOfLines={1}>
+ {member?.user?.first_name} {member?.user?.last_name}
+ </Text>
+ <Text style={styles.lastSeen}>{lastSeen}</Text>
+ </View>
+ </View>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ height: ChatHeaderHeight - StatusBarHeight,
+ flexDirection: 'row',
+ alignItems: 'center',
+ paddingLeft: '15%',
+ },
+ avatar: {
+ width: normalize(40),
+ height: normalize(40),
+ borderRadius: normalize(40) / 2,
+ },
+ online: {
+ position: 'absolute',
+ backgroundColor: '#6EE7E7',
+ width: normalize(16),
+ height: normalize(16),
+ borderRadius: normalize(16) / 2,
+ borderColor: 'white',
+ borderWidth: 3,
+ top: 0,
+ right: 0,
+ },
+ content: {
+ flex: 1,
+ height: '80%',
+ justifyContent: 'space-between',
+ flexDirection: 'column',
+ marginLeft: '5%',
+ },
+ name: {
+ fontWeight: '700',
+ fontSize: normalize(15),
+ lineHeight: normalize(18),
+ },
+ lastSeen: {
+ color: '#828282',
+ fontWeight: '500',
+ fontSize: normalize(12),
+ lineHeight: normalize(14),
+ },
+});
+
+export default ChatHeader;
diff --git a/src/components/messages/MessagesHeader.tsx b/src/components/messages/MessagesHeader.tsx
new file mode 100644
index 00000000..660da97d
--- /dev/null
+++ b/src/components/messages/MessagesHeader.tsx
@@ -0,0 +1,59 @@
+import React, {Fragment, useContext} from 'react';
+import {StyleSheet, View} from 'react-native';
+import {Text} from 'react-native-animatable';
+import {TouchableOpacity} from 'react-native-gesture-handler';
+import {normalize} from '../../utils';
+import ComposeIcon from '../../assets/icons/compose.svg';
+import {ChatContext} from '../../App';
+
+type MessagesHeaderProps = {
+ createChannel: () => void;
+};
+
+const MessagesHeader: React.FC<MessagesHeaderProps> = ({createChannel}) => {
+ const {chatClient} = useContext(ChatContext);
+ const unread = chatClient.user?.total_unread_count as number;
+ return (
+ <View style={styles.header}>
+ <Text style={styles.headerText}>Messages</Text>
+ {unread && unread !== 0 ? (
+ <Text style={styles.unreadText}>
+ {unread > 99 ? '99+' : unread} unread
+ </Text>
+ ) : (
+ <Fragment />
+ )}
+ <View style={styles.flex} />
+ <TouchableOpacity style={styles.compose} onPress={createChannel}>
+ <ComposeIcon width={normalize(20)} height={normalize(20)} />
+ </TouchableOpacity>
+ </View>
+ );
+};
+
+const styles = StyleSheet.create({
+ flex: {
+ flex: 1,
+ },
+ header: {
+ marginHorizontal: '8%',
+ marginTop: '5%',
+ alignItems: 'center',
+ flexDirection: 'row',
+ },
+ headerText: {
+ fontWeight: '700',
+ fontSize: normalize(18),
+ lineHeight: normalize(21),
+ },
+ unreadText: {
+ color: '#8F01FF',
+ marginLeft: 10,
+ fontWeight: '700',
+ lineHeight: normalize(17),
+ fontSize: normalize(14),
+ },
+ compose: {},
+});
+
+export default MessagesHeader;
diff --git a/src/components/messages/index.ts b/src/components/messages/index.ts
new file mode 100644
index 00000000..e194093c
--- /dev/null
+++ b/src/components/messages/index.ts
@@ -0,0 +1,2 @@
+export {default as MessagesHeader} from './MessagesHeader';
+export {default as ChannelPreview} from './ChannelPreview';
diff --git a/src/components/profile/Content.tsx b/src/components/profile/Content.tsx
index 9c33eabc..05098d14 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,12 +60,14 @@ 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 [socialsBarHeight, setSocialsBarHeight] = useState(0);
const [shouldBounce, setShouldBounce] = useState<boolean>(true);
const [refreshing, setRefreshing] = useState<boolean>(false);
@@ -88,6 +90,11 @@ const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => {
setProfileBodyHeight(height);
};
+ const onSocialsBarLayout = (e: LayoutChangeEvent) => {
+ const {height} = e.nativeEvent.layout;
+ setSocialsBarHeight(height);
+ };
+
useEffect(() => {
const isActuallyBlocked = blockedUsers.some(
(cur_user) => user.username === cur_user.username,
@@ -103,45 +110,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 +159,7 @@ const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => {
/>
<TaggsBar
{...{y, profileBodyHeight, userXId, screenType}}
- whiteRing={undefined}
+ onLayout={onSocialsBarLayout}
/>
{canViewProfile(state, userXId, screenType) ? (
<PublicProfile
@@ -175,6 +169,7 @@ const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => {
screenType,
setScrollEnabled,
profileBodyHeight,
+ socialsBarHeight,
scrollViewRef,
}}
/>
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/profile/Friends.tsx b/src/components/profile/Friends.tsx
index 44f6bb48..c1dca755 100644
--- a/src/components/profile/Friends.tsx
+++ b/src/components/profile/Friends.tsx
@@ -27,7 +27,7 @@ interface FriendsProps {
const Friends: React.FC<FriendsProps> = ({result, screenType, userId}) => {
const state: RootState = useStore().getState();
const dispatch = useDispatch();
- const {user: loggedInUser = NO_USER} = state;
+ const {user: loggedInUser = NO_USER} = state.user;
const navigation = useNavigation();
const [usersFromContacts, setUsersFromContacts] = useState<
ProfilePreviewType[]
@@ -39,7 +39,7 @@ const Friends: React.FC<FriendsProps> = ({result, screenType, userId}) => {
const permission = await checkPermission();
if (permission === 'authorized') {
let response = await usersFromContactsService(contacts);
- await setUsersFromContacts(response.existing_tagg_users);
+ setUsersFromContacts(response.existing_tagg_users);
} else {
console.log('Authorize access to contacts');
}
@@ -84,10 +84,10 @@ const Friends: React.FC<FriendsProps> = ({result, screenType, userId}) => {
return (
<>
- {loggedInUser.userId === userId && (
+ {loggedInUser.userId === userId && usersFromContacts.length !== 0 && (
<View style={styles.subheader}>
<View style={styles.addFriendHeaderContainer}>
- <Text style={[styles.subheaderText]}>Contacts on tagg</Text>
+ <Text style={[styles.subheaderText]}>Contacts on Tagg</Text>
</View>
<UsersFromContacts />
</View>
diff --git a/src/components/profile/PublicProfile.tsx b/src/components/profile/PublicProfile.tsx
index 88e0ecd1..1c49bff5 100644
--- a/src/components/profile/PublicProfile.tsx
+++ b/src/components/profile/PublicProfile.tsx
@@ -35,6 +35,7 @@ const PublicProfile: React.FC<ContentProps> = ({
screenType,
setScrollEnabled,
profileBodyHeight,
+ socialsBarHeight,
scrollViewRef,
}) => {
const dispatch = useDispatch();
@@ -99,11 +100,12 @@ const PublicProfile: React.FC<ContentProps> = ({
scrollViewRef.current
) {
setScrollEnabled(false);
- scrollViewRef.current.getNode().scrollTo({y: 0});
+ scrollViewRef.current.scrollTo({y: 0});
navigation.navigate('MomentUploadPrompt', {
screenType,
momentCategory: momentCategories[0],
profileBodyHeight,
+ socialsBarHeight,
});
setIsStageOnePromptClosed(true);
}
@@ -133,6 +135,7 @@ const PublicProfile: React.FC<ContentProps> = ({
navigation,
screenType,
profileBodyHeight,
+ socialsBarHeight,
scrollViewRef,
]),
);
diff --git a/src/components/search/RecentSearches.tsx b/src/components/search/RecentSearches.tsx
index 84d35cac..6cea9338 100644
--- a/src/components/search/RecentSearches.tsx
+++ b/src/components/search/RecentSearches.tsx
@@ -6,6 +6,7 @@ import {
StyleSheet,
TouchableOpacityProps,
ScrollView,
+ Keyboard,
} from 'react-native';
import {
PreviewType,
@@ -15,7 +16,7 @@ import {
} from '../../types';
import {TAGG_LIGHT_BLUE} from '../../constants';
import SearchResults from './SearchResults';
-import {SCREEN_HEIGHT} from '../../utils';
+import {useBottomTabBarHeight} from '@react-navigation/bottom-tabs';
interface RecentSearchesProps extends TouchableOpacityProps {
sectionTitle: PreviewType;
@@ -25,35 +26,25 @@ interface RecentSearchesProps extends TouchableOpacityProps {
}
const RecentSearches: React.FC<RecentSearchesProps> = (props) => {
- const {sectionTitle, recents, recentCategories, screenType} = props;
+ const {recents, recentCategories} = props;
return (
- <ScrollView
- style={styles.mainContainer}
- contentContainerStyle={styles.contentContainer}>
+ <>
<View style={styles.header}>
- <Text style={styles.title}>{sectionTitle}</Text>
+ <Text style={styles.title}>Recent</Text>
<TouchableOpacity {...props}>
<Text style={styles.clear}>Clear all</Text>
</TouchableOpacity>
</View>
- <SearchResults
- results={recents}
- categories={recentCategories}
- previewType={sectionTitle}
- screenType={screenType}
- />
- </ScrollView>
+ <ScrollView
+ onScrollBeginDrag={Keyboard.dismiss}
+ contentContainerStyle={{paddingBottom: useBottomTabBarHeight()}}>
+ <SearchResults results={recents} categories={recentCategories} />
+ </ScrollView>
+ </>
);
};
const styles = StyleSheet.create({
- mainContainer: {
- flex: 1,
- },
- contentContainer: {
- paddingBottom: SCREEN_HEIGHT * 0.1,
- flex: 1,
- },
header: {
paddingHorizontal: 25,
paddingVertical: 5,
diff --git a/src/components/search/SearchBar.tsx b/src/components/search/SearchBar.tsx
index 4824b56f..d441b07b 100644
--- a/src/components/search/SearchBar.tsx
+++ b/src/components/search/SearchBar.tsx
@@ -9,20 +9,23 @@ import {
TextInputSubmitEditingEventData,
TouchableOpacity,
View,
+ ViewStyle,
+ LayoutChangeEvent,
} from 'react-native';
import {normalize} from 'react-native-elements';
-import Animated, {interpolate} from 'react-native-reanimated';
+import Animated, {useAnimatedStyle} from 'react-native-reanimated';
import Icon from 'react-native-vector-icons/Feather';
import {useSelector} from 'react-redux';
import {RootState} from '../../store/rootReducer';
-import {getSearchSuggestions, SCREEN_HEIGHT} from '../../utils';
+import {getSearchSuggestions} from '../../utils';
const AnimatedIcon = Animated.createAnimatedComponent(Icon);
interface SearchBarProps extends TextInputProps {
onCancel: () => void;
- top: Animated.Value<number>;
+ animationProgress: Animated.SharedValue<number>;
searching: boolean;
+ onLayout: (e: LayoutChangeEvent) => void;
}
const SearchBar: React.FC<SearchBarProps> = ({
onFocus,
@@ -31,7 +34,8 @@ const SearchBar: React.FC<SearchBarProps> = ({
value,
onCancel,
searching,
- top,
+ animationProgress,
+ onLayout,
}) => {
const handleSubmit = (
e: NativeSyntheticEvent<TextInputSubmitEditingEventData>,
@@ -107,19 +111,15 @@ const SearchBar: React.FC<SearchBarProps> = ({
}, [searching]);
/*
- * Animated nodes used in search bar activation animation.
+ * On-search marginRight style ("cancel" button slides and fades in).
*/
- const marginRight: Animated.Node<number> = interpolate(top, {
- inputRange: [-SCREEN_HEIGHT, 0],
- outputRange: [0, 58],
- });
- const opacity: Animated.Node<number> = interpolate(top, {
- inputRange: [-SCREEN_HEIGHT, 0],
- outputRange: [0, 1],
- });
+ const animatedStyles = useAnimatedStyle<ViewStyle>(() => ({
+ marginRight: animationProgress.value * 58,
+ opacity: animationProgress.value,
+ }));
return (
- <View style={styles.container}>
+ <View style={styles.container} onLayout={onLayout}>
<Animated.View style={styles.inputContainer}>
<AnimatedIcon
name="search"
@@ -131,13 +131,13 @@ const SearchBar: React.FC<SearchBarProps> = ({
style={[styles.input]}
placeholderTextColor={'#828282'}
onSubmitEditing={handleSubmit}
- clearButtonMode="while-editing"
+ clearButtonMode="always"
autoCapitalize="none"
autoCorrect={false}
{...{placeholder, value, onChangeText, onFocus, onBlur}}
/>
</Animated.View>
- <Animated.View style={{marginRight, opacity}}>
+ <Animated.View style={animatedStyles}>
<TouchableOpacity style={styles.cancelButton} onPress={onCancel}>
<Text style={styles.cancelText}>Cancel</Text>
</TouchableOpacity>
@@ -151,6 +151,7 @@ const styles = StyleSheet.create({
height: 40,
paddingHorizontal: 20,
flexDirection: 'row',
+ zIndex: 2,
},
inputContainer: {
flexGrow: 1,
diff --git a/src/components/search/SearchCategories.tsx b/src/components/search/SearchCategories.tsx
index c747b34f..3d142981 100644
--- a/src/components/search/SearchCategories.tsx
+++ b/src/components/search/SearchCategories.tsx
@@ -3,29 +3,40 @@ import React, {useEffect, useState} from 'react';
import {StyleSheet, View} from 'react-native';
import {getSuggestedSearchBubbleSuggestions} from '../../services/ExploreService';
import {SearchCategoryType} from '../../types';
-import {SCREEN_WIDTH} from '../../utils';
import GradientBorderButton from '../common/GradientBorderButton';
+import {useSelector} from 'react-redux';
+import {RootState} from 'src/store/rootReducer';
interface SearchCategoriesProps {
darkStyle?: boolean;
- defaultButtons?: SearchCategoryType[];
+ useSuggestions: boolean;
}
const SearchCategories: React.FC<SearchCategoriesProps> = ({
darkStyle = false,
- defaultButtons,
+ useSuggestions,
}) => {
const navigation = useNavigation();
- const mtSearchCategory: (key: number) => SearchCategoryType = (key) => ({
+ const {
+ profile: {university = ''},
+ } = useSelector((state: RootState) => state.user);
+ const defaultButtons: SearchCategoryType[] = [21, 22, 23, 24].map(
+ (year, index) => ({
+ id: index * -1,
+ name: `${university.split(' ')[0]} '${year}`,
+ category: university,
+ }),
+ );
+ const createloadingCategory: (key: number) => SearchCategoryType = (key) => ({
id: key,
name: '...',
category: '...',
});
const [buttons, setButtons] = useState<SearchCategoryType[]>([
- mtSearchCategory(-1),
- mtSearchCategory(-2),
- mtSearchCategory(-3),
- mtSearchCategory(-4),
+ createloadingCategory(1),
+ createloadingCategory(2),
+ createloadingCategory(3),
+ createloadingCategory(4),
]);
useEffect(() => {
@@ -36,7 +47,7 @@ const SearchCategories: React.FC<SearchCategoriesProps> = ({
setButtons(localButtons);
}
};
- if (!defaultButtons) {
+ if (useSuggestions) {
loadButtons();
} else {
setButtons(defaultButtons);
@@ -45,33 +56,34 @@ const SearchCategories: React.FC<SearchCategoriesProps> = ({
return (
<View style={styles.container}>
- {buttons.map((searchCategory) => (
- <GradientBorderButton
- key={searchCategory.id}
- text={searchCategory.name}
- darkStyle={darkStyle}
- onPress={() => {
- if (searchCategory.name !== '...') {
- navigation.push('DiscoverUsers', {
- searchCategory,
- });
- }
- }}
- />
- ))}
+ <View style={styles.categoryContainer}>
+ {buttons.map((searchCategory, index) => (
+ <GradientBorderButton
+ key={index}
+ text={searchCategory.name}
+ darkStyle={darkStyle}
+ onPress={() => {
+ if (searchCategory.name !== '...') {
+ navigation.push('DiscoverUsers', {
+ searchCategory,
+ });
+ }
+ }}
+ />
+ ))}
+ </View>
</View>
);
};
const styles = StyleSheet.create({
container: {
- zIndex: 0,
- top: '3%',
- alignSelf: 'center',
+ paddingVertical: 20,
+ },
+ categoryContainer: {
flexDirection: 'row',
- width: SCREEN_WIDTH * 0.9,
- flexWrap: 'wrap',
justifyContent: 'space-evenly',
+ flexWrap: 'wrap',
},
});
export default SearchCategories;
diff --git a/src/components/search/SearchResultList.tsx b/src/components/search/SearchResultList.tsx
index 687b2285..a32760e1 100644
--- a/src/components/search/SearchResultList.tsx
+++ b/src/components/search/SearchResultList.tsx
@@ -1,15 +1,23 @@
import React, {useEffect, useState} from 'react';
-import {SectionList, StyleSheet, Text, View} from 'react-native';
+import {
+ SectionList,
+ StyleSheet,
+ Text,
+ View,
+ Keyboard,
+ SectionListData,
+} from 'react-native';
import {useSelector} from 'react-redux';
import {RootState} from '../../store/rootreducer';
import {NO_RESULTS_FOUND} from '../../constants/strings';
import {PreviewType, ScreenType} from '../../types';
-import {normalize, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
+import {normalize, SCREEN_WIDTH} from '../../utils';
import SearchResultsCell from './SearchResultCell';
+import {useBottomTabBarHeight} from '@react-navigation/bottom-tabs';
interface SearchResultsProps {
- results: Array<any> | undefined;
- keyboardVisible: boolean;
+ // TODO: make sure results come in as same type, regardless of profile, category, badges
+ results: SectionListData<any>[];
previewType: PreviewType;
screenType: ScreenType;
}
@@ -21,11 +29,8 @@ const sectionHeader: React.FC<Boolean> = (showBorder: Boolean) => {
return null;
};
-const SearchResultList: React.FC<SearchResultsProps> = ({
- results,
- keyboardVisible,
-}) => {
- const [showEmptyView, setshowEmptyView] = useState(false);
+const SearchResultList: React.FC<SearchResultsProps> = ({results}) => {
+ const [showEmptyView, setshowEmptyView] = useState<boolean>(false);
const {user: loggedInUser} = useSelector((state: RootState) => state.user);
useEffect(() => {
@@ -38,57 +43,41 @@ const SearchResultList: React.FC<SearchResultsProps> = ({
}
}, [results]);
- return (
- <View style={styles.container}>
- {showEmptyView && (
- <View style={styles.noResultsTextContainer}>
- <Text style={styles.noResultsTextStyle}>{NO_RESULTS_FOUND}</Text>
- </View>
- )}
- {!showEmptyView && (
- <SectionList
- style={[
- {width: SCREEN_WIDTH},
- keyboardVisible ? styles.keyboardOpen : {},
- ]}
- contentContainerStyle={styles.sectionListContentContainer}
- sections={results}
- keyExtractor={(item, index) => item.id + index}
- renderItem={({section, item}) => {
- return (
- <SearchResultsCell
- profileData={item}
- loggedInUser={loggedInUser}
- />
- );
- }}
- renderSectionHeader={({section: {data}}) =>
- sectionHeader(data.length !== 0)
- }
- />
- )}
+ return showEmptyView ? (
+ <View style={styles.container} onTouchStart={Keyboard.dismiss}>
+ <Text style={styles.noResultsTextStyle}>{NO_RESULTS_FOUND}</Text>
</View>
+ ) : (
+ <SectionList
+ onScrollBeginDrag={Keyboard.dismiss}
+ contentContainerStyle={[{paddingBottom: useBottomTabBarHeight()}]}
+ sections={results}
+ keyExtractor={(item, index) => item.id + index}
+ renderItem={({item}) => {
+ return (
+ <SearchResultsCell profileData={item} loggedInUser={loggedInUser} />
+ );
+ }}
+ renderSectionHeader={({section: {data}}) =>
+ sectionHeader(data.length !== 0)
+ }
+ stickySectionHeadersEnabled={false}
+ />
);
};
const styles = StyleSheet.create({
container: {
- height: SCREEN_HEIGHT,
- paddingBottom: SCREEN_HEIGHT * 0.1,
- },
- sectionListContentContainer: {
- paddingBottom: SCREEN_HEIGHT * 0.15,
- width: SCREEN_WIDTH,
+ flex: 1,
+ marginTop: 30,
+ alignItems: 'center',
},
sectionHeaderStyle: {
width: '100%',
height: 0.5,
- marginBottom: normalize(24),
+ marginVertical: 5,
backgroundColor: '#C4C4C4',
},
- keyboardOpen: {
- marginBottom: SCREEN_HEIGHT * 0.35,
- },
noResultsTextContainer: {
justifyContent: 'center',
flexDirection: 'row',
diff --git a/src/components/search/SearchResults.tsx b/src/components/search/SearchResults.tsx
index ef518d8b..a73d0b40 100644
--- a/src/components/search/SearchResults.tsx
+++ b/src/components/search/SearchResults.tsx
@@ -1,18 +1,10 @@
import React from 'react';
-import {
- ProfilePreviewType,
- PreviewType,
- ScreenType,
- CategoryPreviewType,
-} from '../../types';
-import {View} from 'react-native';
+import {ProfilePreviewType, CategoryPreviewType} from '../../types';
import SearchResultsCell from './SearchResultCell';
import {useSelector} from 'react-redux';
import {RootState} from '../../store/rootReducer';
interface SearchResultsProps {
results: ProfilePreviewType[];
- previewType: PreviewType;
- screenType: ScreenType;
categories: CategoryPreviewType[];
}
const SearchResults: React.FC<SearchResultsProps> = ({results, categories}) => {
@@ -22,7 +14,7 @@ const SearchResults: React.FC<SearchResultsProps> = ({results, categories}) => {
*/
const {user: loggedInUser} = useSelector((state: RootState) => state.user);
return (
- <View>
+ <>
{categories
.slice(0)
.reverse()
@@ -43,7 +35,7 @@ const SearchResults: React.FC<SearchResultsProps> = ({results, categories}) => {
{...{loggedInUser}}
/>
))}
- </View>
+ </>
);
};
diff --git a/src/components/search/SearchResultsBackground.tsx b/src/components/search/SearchResultsBackground.tsx
index 2833553d..e5236295 100644
--- a/src/components/search/SearchResultsBackground.tsx
+++ b/src/components/search/SearchResultsBackground.tsx
@@ -1,28 +1,55 @@
import React from 'react';
-import {StyleSheet} from 'react-native';
-import Animated, {interpolate} from 'react-native-reanimated';
-import {SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils';
+import {StyleSheet, ViewStyle} from 'react-native';
+import Animated, {
+ useAnimatedStyle,
+ useDerivedValue,
+ interpolate,
+ Extrapolate,
+} from 'react-native-reanimated';
+import {useSafeAreaInsets} from 'react-native-safe-area-context';
interface SearchResultsBackgroundProps {
- top: Animated.Value<number>;
+ animationProgress: Animated.SharedValue<number>;
+ searchBarHeight: number;
+ searching: boolean;
}
const SearchResultsBackground: React.FC<SearchResultsBackgroundProps> = ({
- top,
+ animationProgress,
+ searchBarHeight,
+ searching,
children,
}) => {
- const opacityBackground: Animated.Node<number> = interpolate(top, {
- inputRange: [-SCREEN_HEIGHT, 0],
- outputRange: [0, 1],
- });
- const opacityContent: Animated.Node<number> = interpolate(top, {
- inputRange: [-SCREEN_HEIGHT / 40, 0],
- outputRange: [0, 1],
- });
+ const {top: topInset} = useSafeAreaInsets();
+ /*
+ * On-search container style (opacity fade-in).
+ */
+ const backgroundAnimatedStyles = useAnimatedStyle<ViewStyle>(() => ({
+ opacity: animationProgress.value,
+ }));
+ /*
+ * Derived animation value for contentAnimatedStyles.
+ */
+ const contentAnimationProgress = useDerivedValue<number>(() =>
+ interpolate(animationProgress.value, [0.9, 1], [0, 1], Extrapolate.CLAMP),
+ );
+ /*
+ * On-search content style (delayed opacity fade-in).
+ */
+ const contentAnimatedStyles = useAnimatedStyle<ViewStyle>(() => ({
+ opacity: contentAnimationProgress.value,
+ }));
return (
<Animated.View
- style={[styles.container, {opacity: opacityBackground, top}]}>
- <Animated.View
- style={[styles.contentContainer, {opacity: opacityContent}]}>
+ style={[
+ styles.container,
+ backgroundAnimatedStyles,
+ {
+ // absolute: inset + search screen paddingTop + searchBar + padding
+ paddingTop: topInset + 15 + searchBarHeight + 10,
+ },
+ ]}
+ pointerEvents={searching ? 'auto' : 'none'}>
+ <Animated.View style={[styles.contentContainer, contentAnimatedStyles]}>
{children}
</Animated.View>
</Animated.View>
@@ -30,15 +57,11 @@ const SearchResultsBackground: React.FC<SearchResultsBackgroundProps> = ({
};
const styles = StyleSheet.create({
container: {
- height: SCREEN_HEIGHT,
- width: SCREEN_WIDTH,
- position: 'absolute',
+ ...StyleSheet.absoluteFillObject,
backgroundColor: 'white',
},
contentContainer: {
flex: 1,
- paddingVertical: 10,
- paddingBottom: SCREEN_HEIGHT / 15,
},
});
export default SearchResultsBackground;
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 06acadc1..4d567b25 100644
--- a/src/components/taggs/TaggsBar.tsx
+++ b/src/components/taggs/TaggsBar.tsx
@@ -1,6 +1,11 @@
-import React, {Fragment, useEffect, useState} from 'react';
-import {StyleSheet} from 'react-native';
-import Animated from 'react-native-reanimated';
+import React, {useEffect, useState} from 'react';
+import {LayoutChangeEvent, StyleSheet} from 'react-native';
+import Animated, {
+ Extrapolate,
+ interpolate,
+ useAnimatedStyle,
+ useDerivedValue,
+} from 'react-native-reanimated';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import {useDispatch, useSelector} from 'react-redux';
import {
@@ -14,22 +19,22 @@ import {RootState} from '../../store/rootReducer';
import {ScreenType} from '../../types';
import Tagg from './Tagg';
-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[];
+ onLayout: (event: LayoutChangeEvent) => void;
}
const TaggsBar: React.FC<TaggsBarProps> = ({
y,
profileBodyHeight,
userXId,
screenType,
- whiteRing,
linkedSocials,
+ onLayout,
}) => {
const dispatch = useDispatch();
let [taggs, setTaggs] = useState<Object[]>([]);
@@ -37,7 +42,7 @@ const TaggsBar: React.FC<TaggsBarProps> = ({
const {user} = useSelector((state: RootState) =>
userXId ? state.userX[screenType][userXId] : state.user,
);
-
+ const insetTop = useSafeAreaInsets().top;
/**
* Updates the individual social that needs update
* If username is empty, update nonintegrated socials like Snapchat and TikTok
@@ -77,12 +82,12 @@ const TaggsBar: React.FC<TaggsBarProps> = ({
isIntegrated={INTEGRATED_SOCIAL_LIST.indexOf(social) !== -1}
setTaggsNeedUpdate={setTaggsNeedUpdate}
setSocialDataNeedUpdate={handleSocialUpdate}
- whiteRing={whiteRing ? whiteRing : undefined}
+ whiteRing={false}
/>,
);
i++;
}
- if (!userXId && !whiteRing) {
+ if (!userXId) {
for (let social of unlinkedSocials) {
new_taggs.push(
<Tagg
@@ -95,7 +100,7 @@ const TaggsBar: React.FC<TaggsBarProps> = ({
userXId={userXId}
screenType={screenType}
user={user}
- whiteRing={whiteRing ? whiteRing : undefined}
+ whiteRing={false}
/>,
);
i++;
@@ -108,64 +113,55 @@ const TaggsBar: React.FC<TaggsBarProps> = ({
loadData();
}
}, [taggsNeedUpdate, user]);
-
- 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,
- });
- 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]} onLayout={onLayout}>
<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,
},
});