aboutsummaryrefslogtreecommitdiff
path: root/src/screens
diff options
context:
space:
mode:
Diffstat (limited to 'src/screens')
-rw-r--r--src/screens/chat/ChatListScreen.tsx134
-rw-r--r--src/screens/chat/ChatResultsCell.tsx117
-rw-r--r--src/screens/chat/ChatResultsList.tsx102
-rw-r--r--src/screens/chat/ChatScreen.tsx53
-rw-r--r--src/screens/chat/ChatSearchBar.tsx112
-rw-r--r--src/screens/chat/NewChatModal.tsx161
-rw-r--r--src/screens/chat/index.ts6
-rw-r--r--src/screens/index.ts1
-rw-r--r--src/screens/main/NotificationsScreen.tsx2
-rw-r--r--src/screens/onboarding/Login.tsx1
-rw-r--r--src/screens/profile/IndividualMoment.tsx14
-rw-r--r--src/screens/profile/InviteFriendsScreen.tsx2
-rw-r--r--src/screens/profile/MomentUploadPromptScreen.tsx20
-rw-r--r--src/screens/profile/ProfileScreen.tsx36
-rw-r--r--src/screens/profile/SettingsScreen.tsx6
-rw-r--r--src/screens/search/DiscoverUsers.tsx2
-rw-r--r--src/screens/search/SearchScreen.tsx180
-rw-r--r--src/screens/suggestedPeople/SPBody.tsx20
-rw-r--r--src/screens/suggestedPeople/SuggestedPeopleScreen.tsx2
19 files changed, 776 insertions, 195 deletions
diff --git a/src/screens/chat/ChatListScreen.tsx b/src/screens/chat/ChatListScreen.tsx
new file mode 100644
index 00000000..daea9984
--- /dev/null
+++ b/src/screens/chat/ChatListScreen.tsx
@@ -0,0 +1,134 @@
+import AsyncStorage from '@react-native-community/async-storage';
+import {StackNavigationProp} from '@react-navigation/stack';
+import React, {useContext, useEffect, useMemo, useState} from 'react';
+import {SafeAreaView, StatusBar, StyleSheet, View} from 'react-native';
+import {useStore} from 'react-redux';
+import {ChannelList, Chat} from 'stream-chat-react-native';
+import {ChatContext} from '../../App';
+import {TabsGradient} from '../../components';
+import {ChannelPreview, MessagesHeader} from '../../components/messages';
+import {MainStackParams} from '../../routes';
+import {RootState} from '../../store/rootReducer';
+import {
+ LocalAttachmentType,
+ LocalChannelType,
+ LocalCommandType,
+ LocalEventType,
+ LocalMessageType,
+ LocalReactionType,
+ LocalUserType,
+} from '../../types';
+
+import NewChatModal from './NewChatModal';
+type ChatListScreenNavigationProp = StackNavigationProp<
+ MainStackParams,
+ 'ChatList'
+>;
+interface ChatListScreenProps {
+ navigation: ChatListScreenNavigationProp;
+}
+/*
+ * Screen that displays all of the user's active conversations.
+ */
+const ChatListScreen: React.FC<ChatListScreenProps> = ({navigation}) => {
+ const {chatClient, setChannel} = useContext(ChatContext);
+ const [modalVisible, setChatModalVisible] = useState(false);
+
+ const [clientReady, setClientReady] = useState(false);
+ const state: RootState = useStore().getState();
+ const loggedInUserId = state.user.user.userId;
+
+ const memoizedFilters = useMemo(
+ () => ({
+ members: {$in: [loggedInUserId]},
+ type: 'messaging',
+ }),
+ [],
+ );
+
+ useEffect(() => {
+ const setupClient = async () => {
+ const chatToken = await AsyncStorage.getItem('chatToken');
+ await chatClient.connectUser(
+ {
+ id: loggedInUserId,
+ },
+ chatToken,
+ );
+ return setClientReady(true);
+ };
+ if (!clientReady) {
+ setupClient().catch((err) => {
+ console.error(err);
+ });
+ }
+ }, []);
+
+ return (
+ <View style={styles.background}>
+ <SafeAreaView>
+ <StatusBar barStyle="dark-content" />
+ <MessagesHeader
+ createChannel={() => {
+ setChatModalVisible(true);
+ }}
+ />
+ {clientReady && (
+ <Chat client={chatClient}>
+ <View style={styles.chatContainer}>
+ <ChannelList<
+ LocalAttachmentType,
+ LocalChannelType,
+ LocalCommandType,
+ LocalEventType,
+ LocalMessageType,
+ LocalReactionType,
+ LocalUserType
+ >
+ filters={memoizedFilters}
+ options={{
+ presence: true,
+ state: true,
+ watch: true,
+ }}
+ sort={{last_message_at: -1}}
+ maxUnreadCount={99}
+ Preview={ChannelPreview}
+ />
+ </View>
+ </Chat>
+ )}
+ <NewChatModal {...{modalVisible, setChatModalVisible}} />
+ </SafeAreaView>
+ <TabsGradient />
+ </View>
+ );
+};
+
+const styles = StyleSheet.create({
+ background: {
+ flex: 1,
+ backgroundColor: 'white',
+ },
+ container: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ placeholder: {
+ fontSize: 14,
+ fontWeight: 'bold',
+ marginBottom: 10,
+ },
+ button: {
+ backgroundColor: '#CCE4FC',
+ padding: 15,
+ borderRadius: 5,
+ },
+ chatContainer: {
+ height: '100%',
+ marginTop: 10,
+ },
+});
+
+export default ChatListScreen;
diff --git a/src/screens/chat/ChatResultsCell.tsx b/src/screens/chat/ChatResultsCell.tsx
new file mode 100644
index 00000000..d947c122
--- /dev/null
+++ b/src/screens/chat/ChatResultsCell.tsx
@@ -0,0 +1,117 @@
+import {useNavigation} from '@react-navigation/native';
+import React, {useContext, useEffect, useState} from 'react';
+import {Alert, Image, StyleSheet, Text, View} from 'react-native';
+import {TouchableOpacity} from 'react-native-gesture-handler';
+import {ChatContext} from '../../App';
+import {ERROR_FAILED_TO_CREATE_CHANNEL} from '../../constants/strings';
+import {loadImageFromURL} from '../../services';
+import {ProfilePreviewType, UserType} from '../../types';
+import {createChannel, normalize, SCREEN_WIDTH} from '../../utils';
+import {defaultUserProfile} from '../../utils/users';
+
+interface ChatResults {
+ profileData: ProfilePreviewType;
+ loggedInUser: UserType;
+ setChatModalVisible: Function;
+}
+
+const ChatResultsCell: React.FC<ChatResults> = ({
+ profileData: {id, username, first_name, last_name, thumbnail_url},
+ loggedInUser,
+ setChatModalVisible,
+}) => {
+ const [avatar, setAvatar] = useState<string | undefined>(undefined);
+ const {chatClient, setChannel} = useContext(ChatContext);
+
+ useEffect(() => {
+ (async () => {
+ if (thumbnail_url !== undefined) {
+ try {
+ const response = await loadImageFromURL(thumbnail_url);
+ if (response) {
+ setAvatar(response);
+ }
+ } catch (error) {
+ console.log('Error while downloading ', error);
+ throw error;
+ }
+ }
+ })();
+ }, [thumbnail_url]);
+
+ const navigation = useNavigation();
+ const createChannelIfNotPresentAndNavigate = async () => {
+ try {
+ setChatModalVisible(false);
+ const channel = await createChannel(loggedInUser.userId, id, chatClient);
+ setChannel(channel);
+ setTimeout(() => {
+ navigation.navigate('Chat');
+ }, 100);
+ } catch (error) {
+ Alert.alert(ERROR_FAILED_TO_CREATE_CHANNEL);
+ }
+ };
+
+ const userCell = () => {
+ return (
+ <TouchableOpacity
+ onPress={createChannelIfNotPresentAndNavigate}
+ style={styles.cellContainer}>
+ <Image
+ defaultSource={defaultUserProfile()}
+ source={{uri: avatar}}
+ style={styles.imageContainer}
+ />
+ <View style={[styles.initialTextContainer, styles.multiText]}>
+ <Text style={styles.initialTextStyle}>{`@${username}`}</Text>
+ <Text style={styles.secondaryTextStyle}>
+ {first_name + ' ' + last_name}
+ </Text>
+ </View>
+ </TouchableOpacity>
+ );
+ };
+
+ return userCell();
+};
+
+const styles = StyleSheet.create({
+ cellContainer: {
+ flexDirection: 'row',
+ paddingHorizontal: 25,
+ paddingVertical: 15,
+ width: SCREEN_WIDTH,
+ },
+ imageContainer: {
+ width: SCREEN_WIDTH * 0.112,
+ height: SCREEN_WIDTH * 0.112,
+ borderRadius: (SCREEN_WIDTH * 0.112) / 2,
+ },
+ categoryBackground: {
+ backgroundColor: 'rgba(196, 196, 196, 0.45)',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ categoryImage: {
+ width: '40%',
+ height: '40%',
+ },
+ initialTextContainer: {
+ marginLeft: SCREEN_WIDTH * 0.08,
+ flexDirection: 'column',
+ justifyContent: 'center',
+ },
+ initialTextStyle: {
+ fontWeight: '500',
+ fontSize: normalize(14),
+ },
+ secondaryTextStyle: {
+ fontWeight: '500',
+ fontSize: normalize(12),
+ color: '#828282',
+ },
+ multiText: {justifyContent: 'space-between'},
+});
+
+export default ChatResultsCell;
diff --git a/src/screens/chat/ChatResultsList.tsx b/src/screens/chat/ChatResultsList.tsx
new file mode 100644
index 00000000..b9970772
--- /dev/null
+++ b/src/screens/chat/ChatResultsList.tsx
@@ -0,0 +1,102 @@
+import {useBottomTabBarHeight} from '@react-navigation/bottom-tabs';
+import React, {useEffect, useState} from 'react';
+import {
+ Keyboard,
+ SectionList,
+ SectionListData,
+ StyleSheet,
+ Text,
+ View,
+} from 'react-native';
+import {useSelector} from 'react-redux';
+import {NO_RESULTS_FOUND} from '../../constants/strings';
+import {RootState} from '../../store/rootreducer';
+import {PreviewType, ScreenType} from '../../types';
+import {normalize, SCREEN_WIDTH} from '../../utils';
+import ChatResultsCell from './ChatResultsCell';
+
+interface ChatResultsProps {
+ // TODO: make sure results come in as same type, regardless of profile, category, badges
+ results: SectionListData<any>[];
+ previewType: PreviewType;
+ screenType: ScreenType;
+ setChatModalVisible: Function;
+}
+
+const ChatResultsList: React.FC<ChatResultsProps> = ({
+ results,
+ setChatModalVisible,
+}) => {
+ const [showEmptyView, setshowEmptyView] = useState<boolean>(false);
+ const {user: loggedInUser} = useSelector((state: RootState) => state.user);
+ const tabbarHeight = useBottomTabBarHeight();
+
+ useEffect(() => {
+ if (results && results.length > 0) {
+ let showEmpty = true;
+
+ results.forEach((e) => {
+ if (e.data.length > 0) {
+ showEmpty = false;
+ }
+ });
+ setshowEmptyView(showEmpty);
+ }
+ }, [results]);
+
+ return showEmptyView ? (
+ <View style={styles.container} onTouchStart={Keyboard.dismiss}>
+ <Text style={styles.noResultsTextStyle}>{NO_RESULTS_FOUND}</Text>
+ </View>
+ ) : (
+ <SectionList
+ onScrollBeginDrag={Keyboard.dismiss}
+ contentContainerStyle={[{paddingBottom: tabbarHeight}]}
+ sections={results}
+ keyExtractor={(item, index) => item.id + index}
+ renderItem={({item}) => (
+ <ChatResultsCell
+ profileData={item}
+ setChatModalVisible={setChatModalVisible}
+ loggedInUser={loggedInUser}
+ />
+ )}
+ stickySectionHeadersEnabled={false}
+ ListEmptyComponent={() => (
+ <View style={styles.empty}>
+ <Text>Start a new chat by searching for someone</Text>
+ </View>
+ )}
+ />
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ marginTop: 30,
+ alignItems: 'center',
+ },
+ sectionHeaderStyle: {
+ width: '100%',
+ height: 0.5,
+ marginVertical: 5,
+ backgroundColor: '#C4C4C4',
+ },
+ noResultsTextContainer: {
+ justifyContent: 'center',
+ flexDirection: 'row',
+ width: SCREEN_WIDTH,
+ },
+ noResultsTextStyle: {
+ fontWeight: '500',
+ fontSize: normalize(14),
+ },
+ empty: {
+ marginTop: 20,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+});
+
+export default ChatResultsList;
diff --git a/src/screens/chat/ChatScreen.tsx b/src/screens/chat/ChatScreen.tsx
new file mode 100644
index 00000000..59c53c99
--- /dev/null
+++ b/src/screens/chat/ChatScreen.tsx
@@ -0,0 +1,53 @@
+import {useBottomTabBarHeight} from '@react-navigation/bottom-tabs';
+import {StackNavigationProp} from '@react-navigation/stack';
+import React, {useContext} from 'react';
+import {StyleSheet} from 'react-native';
+import {SafeAreaView} from 'react-native-safe-area-context';
+import {
+ Channel,
+ Chat,
+ MessageInput,
+ MessageList,
+} from 'stream-chat-react-native';
+import {ChatContext} from '../../App';
+import ChatHeader from '../../components/messages/ChatHeader';
+import {MainStackParams} from '../../routes';
+import {isIPhoneX} from '../../utils';
+
+type ChatScreenNavigationProp = StackNavigationProp<MainStackParams, 'Chat'>;
+interface ChatScreenProps {
+ navigation: ChatScreenNavigationProp;
+}
+/*
+ * Screen that displays all of the user's active conversations.
+ */
+const ChatScreen: React.FC<ChatScreenProps> = () => {
+ const {channel, chatClient} = useContext(ChatContext);
+ const tabbarHeight = useBottomTabBarHeight();
+
+ return (
+ <SafeAreaView
+ style={[
+ styles.container,
+ // unable to figure out the padding issue, a hacky solution
+ {paddingBottom: isIPhoneX() ? tabbarHeight + 20 : tabbarHeight + 50},
+ ]}>
+ <ChatHeader />
+ <Chat client={chatClient}>
+ <Channel channel={channel} keyboardVerticalOffset={0}>
+ <MessageList onThreadSelect={() => {}} />
+ <MessageInput />
+ </Channel>
+ </Chat>
+ </SafeAreaView>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ backgroundColor: 'white',
+ flex: 1,
+ },
+});
+
+export default ChatScreen;
diff --git a/src/screens/chat/ChatSearchBar.tsx b/src/screens/chat/ChatSearchBar.tsx
new file mode 100644
index 00000000..4916ec45
--- /dev/null
+++ b/src/screens/chat/ChatSearchBar.tsx
@@ -0,0 +1,112 @@
+import React from 'react';
+import {
+ Keyboard,
+ NativeSyntheticEvent,
+ StyleSheet,
+ Text,
+ TextInput,
+ TextInputProps,
+ TextInputSubmitEditingEventData,
+ TouchableOpacity,
+ View,
+ ViewStyle,
+} from 'react-native';
+import {normalize} from 'react-native-elements';
+import Animated, {useAnimatedStyle} from 'react-native-reanimated';
+
+interface SearchBarProps extends TextInputProps {
+ onCancel: () => void;
+ animationProgress: Animated.SharedValue<number>;
+ searching: boolean;
+ placeholder: string;
+}
+const ChatSearchBar: React.FC<SearchBarProps> = ({
+ onFocus,
+ onBlur,
+ onChangeText,
+ value,
+ onCancel,
+ searching,
+ animationProgress,
+ onLayout,
+ placeholder,
+}) => {
+ const handleSubmit = (
+ e: NativeSyntheticEvent<TextInputSubmitEditingEventData>,
+ ) => {
+ e.preventDefault();
+ Keyboard.dismiss();
+ };
+
+ /*
+ * On-search marginRight style ("cancel" button slides and fades in).
+ */
+ const animatedStyles = useAnimatedStyle<ViewStyle>(() => ({
+ marginRight: animationProgress.value * 58,
+ opacity: animationProgress.value,
+ }));
+
+ return (
+ <View style={styles.container} onLayout={onLayout}>
+ <Animated.View style={styles.inputContainer}>
+ <Animated.View style={styles.searchTextContainer}>
+ <Text style={styles.searchTextStyes}>To:</Text>
+ </Animated.View>
+ <TextInput
+ style={[styles.input]}
+ placeholderTextColor={'#828282'}
+ onSubmitEditing={handleSubmit}
+ clearButtonMode="always"
+ autoCapitalize="none"
+ autoCorrect={false}
+ {...{placeholder, value, onChangeText, onFocus, onBlur}}
+ />
+ </Animated.View>
+ <Animated.View style={animatedStyles}>
+ <TouchableOpacity style={styles.cancelButton} onPress={onCancel}>
+ <Text style={styles.cancelText}>Cancel</Text>
+ </TouchableOpacity>
+ </Animated.View>
+ </View>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ height: 40,
+ paddingHorizontal: 20,
+ flexDirection: 'row',
+ zIndex: 2,
+ },
+ searchTextContainer: {marginHorizontal: 12},
+ searchTextStyes: {fontWeight: 'bold', fontSize: 14, lineHeight: 17},
+ inputContainer: {
+ flexGrow: 1,
+ flexDirection: 'row',
+ alignItems: 'center',
+ paddingHorizontal: 8,
+ borderRadius: 20,
+ backgroundColor: '#F0F0F0',
+ },
+ searchIcon: {
+ marginRight: 8,
+ },
+ input: {
+ flex: 1,
+ fontSize: 16,
+ color: '#000',
+ letterSpacing: normalize(0.5),
+ },
+ cancelButton: {
+ height: '100%',
+ position: 'absolute',
+ justifyContent: 'center',
+ paddingHorizontal: 8,
+ },
+ cancelText: {
+ color: '#818181',
+ fontWeight: '500',
+ },
+});
+
+export default ChatSearchBar;
diff --git a/src/screens/chat/NewChatModal.tsx b/src/screens/chat/NewChatModal.tsx
new file mode 100644
index 00000000..95e46ecd
--- /dev/null
+++ b/src/screens/chat/NewChatModal.tsx
@@ -0,0 +1,161 @@
+import React, {useEffect, useState} from 'react';
+import {
+ Keyboard,
+ SectionListData,
+ StatusBar,
+ StyleSheet,
+ Text,
+ View,
+} from 'react-native';
+import {useSharedValue} from 'react-native-reanimated';
+import {BottomDrawer} from '../../components';
+import {
+ SEARCH_ENDPOINT_MESSAGES,
+ SEARCH_ENDPOINT_SUGGESTED,
+} from '../../constants';
+import {loadSearchResults} from '../../services';
+import {ScreenType} from '../../types';
+import {normalize} from '../../utils';
+import {ChatResultsList, ChatSearchBar} from './index';
+interface NewChatModalProps {
+ modalVisible: boolean;
+ setChatModalVisible: (open: boolean) => void;
+}
+
+const NewChatModal: React.FC<NewChatModalProps> = ({
+ modalVisible,
+ setChatModalVisible,
+}) => {
+ const [searching, setSearching] = useState(false);
+ /*
+ * Animated value
+ */
+ const animationProgress = useSharedValue<number>(0);
+ const [results, setResults] = useState<SectionListData<any>[]>([]);
+ const [query, setQuery] = useState<string>('');
+ const handleFocus = () => {
+ setSearching(true);
+ };
+ const handleBlur = () => {
+ Keyboard.dismiss();
+ };
+ const handleCancel = () => {
+ setSearching(false);
+ };
+
+ const getDefaultSuggested = async () => {
+ const searchResults = await loadSearchResults(
+ `${SEARCH_ENDPOINT_SUGGESTED}`,
+ );
+ console.log(searchResults);
+ const sanitizedResult = [
+ {
+ title: 'users',
+ data: searchResults?.users,
+ },
+ ];
+ console.log(searchResults, sanitizedResult);
+ setResults(sanitizedResult);
+ };
+
+ const getQuerySuggested = async () => {
+ const searchResults = await loadSearchResults(
+ `${SEARCH_ENDPOINT_MESSAGES}?query=${query}`,
+ );
+ if (query.length > 2) {
+ const sanitizedResult = [
+ {
+ title: 'users',
+ data: searchResults?.users,
+ },
+ ];
+ setResults(sanitizedResult);
+ } else {
+ setResults([]);
+ }
+ };
+
+ useEffect(() => {
+ if (query.length === 0) {
+ getDefaultSuggested();
+ }
+
+ if (!searching) {
+ return;
+ }
+
+ if (query.length < 3) {
+ return;
+ }
+ getQuerySuggested();
+ }, [query]);
+
+ const _modalContent = () => {
+ return (
+ <View style={styles.modalShadowContainer}>
+ <View style={styles.titleContainerStyles}>
+ <Text style={styles.titleTextStyles}>New Message</Text>
+ </View>
+ <ChatSearchBar
+ onCancel={handleCancel}
+ onChangeText={setQuery}
+ onBlur={handleBlur}
+ onFocus={handleFocus}
+ value={query}
+ {...{animationProgress, searching}}
+ placeholder={''}
+ />
+ {results.length > 0 && (
+ <View style={styles.headerContainerStyles}>
+ <Text style={styles.headerTextStyles}>Suggested</Text>
+ </View>
+ )}
+ <ChatResultsList
+ {...{results, setChatModalVisible}}
+ previewType={'Search'}
+ screenType={ScreenType.Search}
+ />
+ </View>
+ );
+ };
+
+ return (
+ <View>
+ <StatusBar barStyle="dark-content" />
+ <BottomDrawer
+ initialSnapPosition={'90%'}
+ isOpen={modalVisible}
+ setIsOpen={setChatModalVisible}
+ showHeader={false}>
+ {_modalContent()}
+ </BottomDrawer>
+ </View>
+ );
+};
+
+const styles = StyleSheet.create({
+ modalShadowContainer: {
+ height: '100%',
+ borderRadius: 9,
+ backgroundColor: 'white',
+ },
+ titleContainerStyles: {marginVertical: 24},
+ titleTextStyles: {
+ fontWeight: 'bold',
+ fontSize: normalize(18),
+ lineHeight: normalize(21),
+ textAlign: 'center',
+ },
+ headerContainerStyles: {
+ marginTop: 26,
+ marginBottom: 10,
+ marginHorizontal: 28,
+ },
+ headerTextStyles: {
+ fontWeight: 'bold',
+ fontSize: normalize(17),
+ lineHeight: normalize(20),
+ },
+});
+
+export default NewChatModal;
diff --git a/src/screens/chat/index.ts b/src/screens/chat/index.ts
new file mode 100644
index 00000000..328eb8bf
--- /dev/null
+++ b/src/screens/chat/index.ts
@@ -0,0 +1,6 @@
+export {default as ChatListScreen} from './ChatListScreen';
+export {default as ChatScreen} from './ChatScreen';
+export {default as NewChatModal} from './NewChatModal';
+export {default as ChatSearchBar} from './ChatSearchBar';
+export {default as ChatResultsList} from './ChatResultsList';
+export {default as ChatResultsCell} from './ChatResultsCell';
diff --git a/src/screens/index.ts b/src/screens/index.ts
index 50ada3d1..44ae4b52 100644
--- a/src/screens/index.ts
+++ b/src/screens/index.ts
@@ -5,3 +5,4 @@ export * from './search';
export * from './suggestedPeople';
export * from './suggestedPeopleOnboarding';
export * from './badge';
+export * from './chat';
diff --git a/src/screens/main/NotificationsScreen.tsx b/src/screens/main/NotificationsScreen.tsx
index 48e89f7a..71199c9b 100644
--- a/src/screens/main/NotificationsScreen.tsx
+++ b/src/screens/main/NotificationsScreen.tsx
@@ -330,7 +330,7 @@ const styles = StyleSheet.create({
flexDirection: 'row',
alignItems: 'stretch',
justifyContent: 'space-between',
- width: SCREEN_WIDTH * 0.85,
+ width: SCREEN_WIDTH * 0.9,
},
headerText: {
fontWeight: '700',
diff --git a/src/screens/onboarding/Login.tsx b/src/screens/onboarding/Login.tsx
index 49ca5ff4..dd2bb2e4 100644
--- a/src/screens/onboarding/Login.tsx
+++ b/src/screens/onboarding/Login.tsx
@@ -160,6 +160,7 @@ const Login: React.FC<LoginProps> = ({navigation}: LoginProps) => {
await AsyncStorage.setItem('token', data.token);
await AsyncStorage.setItem('userId', data.UserID);
await AsyncStorage.setItem('username', username);
+ await AsyncStorage.setItem('chatToken', data.chatToken);
}
if (statusCode === 200 && data.isOnboarded) {
diff --git a/src/screens/profile/IndividualMoment.tsx b/src/screens/profile/IndividualMoment.tsx
index 8c1dc327..871d62bf 100644
--- a/src/screens/profile/IndividualMoment.tsx
+++ b/src/screens/profile/IndividualMoment.tsx
@@ -27,7 +27,7 @@ interface IndividualMomentProps {
navigation: IndividualMomentNavigationProp;
}
-const ITEM_HEIGHT = SCREEN_HEIGHT * (9 / 10);
+const ITEM_HEIGHT = SCREEN_HEIGHT * 0.9;
const IndividualMoment: React.FC<IndividualMomentProps> = ({
route,
@@ -40,13 +40,13 @@ const IndividualMoment: React.FC<IndividualMomentProps> = ({
);
const {
user: {username},
- } = userXId
- ? useSelector((state: RootState) => state.userX[screenType][userXId])
- : useSelector((state: RootState) => state.user);
+ } = useSelector((state: RootState) =>
+ userXId ? state.userX[screenType][userXId] : state.user,
+ );
- const {moments} = userXId
- ? useSelector((state: RootState) => state.userX[screenType][userXId])
- : useSelector((state: RootState) => state.moments);
+ const {moments} = useSelector((state: RootState) =>
+ userXId ? state.userX[screenType][userXId] : state.moments,
+ );
const isOwnProfile = username === loggedInUsername;
const momentData = moments.filter(
diff --git a/src/screens/profile/InviteFriendsScreen.tsx b/src/screens/profile/InviteFriendsScreen.tsx
index a9fa1404..ad9e382e 100644
--- a/src/screens/profile/InviteFriendsScreen.tsx
+++ b/src/screens/profile/InviteFriendsScreen.tsx
@@ -203,7 +203,7 @@ const InviteFriendsScreen: React.FC<InviteFriendsScreenProps> = ({route}) => {
</Animated.View>
</View>
<View style={styles.subheader}>
- <Text style={styles.subheaderText}>Contacts on tagg</Text>
+ <Text style={styles.subheaderText}>Contacts on Tagg</Text>
<UsersFromContacts />
</View>
<View style={styles.subheader}>
diff --git a/src/screens/profile/MomentUploadPromptScreen.tsx b/src/screens/profile/MomentUploadPromptScreen.tsx
index f79c81b4..f0aaffc4 100644
--- a/src/screens/profile/MomentUploadPromptScreen.tsx
+++ b/src/screens/profile/MomentUploadPromptScreen.tsx
@@ -8,7 +8,7 @@ import {Moment} from '../../components';
import {Image} from 'react-native-animatable';
import {UPLOAD_MOMENT_PROMPT_ONE_MESSAGE} from '../../constants/strings';
import {PROFILE_CUTOUT_BOTTOM_Y} from '../../constants';
-import {isIPhoneX, normalize} from '../../utils';
+import {normalize} from '../../utils';
type MomentUploadPromptScreenRouteProp = RouteProp<
MainStackParams,
@@ -28,7 +28,12 @@ const MomentUploadPromptScreen: React.FC<MomentUploadPromptScreenProps> = ({
route,
navigation,
}) => {
- const {screenType, momentCategory, profileBodyHeight} = route.params;
+ const {
+ screenType,
+ momentCategory,
+ profileBodyHeight,
+ socialsBarHeight,
+ } = route.params;
return (
<View style={styles.container}>
<CloseIcon
@@ -61,9 +66,7 @@ const MomentUploadPromptScreen: React.FC<MomentUploadPromptScreenProps> = ({
externalStyles={{
container: {
...styles.momentContainer,
- top: isIPhoneX()
- ? profileBodyHeight + 615
- : profileBodyHeight + 500,
+ top: PROFILE_CUTOUT_BOTTOM_Y + profileBodyHeight + socialsBarHeight,
},
titleText: styles.momentHeaderText,
header: styles.momentHeader,
@@ -103,20 +106,21 @@ const styles = StyleSheet.create({
//Styles to adjust moment container
momentScrollContainer: {
backgroundColor: 'transparent',
+ marginTop: 10,
},
momentContainer: {
...StyleSheet.absoluteFillObject,
backgroundColor: 'transparent',
- height: 170,
+ height: 175,
},
momentHeaderText: {
...StyleSheet.absoluteFillObject,
marginLeft: 12,
- marginTop: 10,
+ paddingVertical: 5,
},
momentHeader: {
+ marginTop: 7,
backgroundColor: 'transparent',
- paddingVertical: 20,
},
});
diff --git a/src/screens/profile/ProfileScreen.tsx b/src/screens/profile/ProfileScreen.tsx
index 313e2f2c..6d9ef020 100644
--- a/src/screens/profile/ProfileScreen.tsx
+++ b/src/screens/profile/ProfileScreen.tsx
@@ -1,17 +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
- * including posts, messaging, and settings
- */
type ProfileScreenRouteProps = RouteProp<MainStackParams, 'Profile'>;
@@ -22,32 +13,11 @@ 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.
- * Checks if the required userXId is present in the store, if not userXId is set to dummy id
- */
- // if (userXId && !(userXId in useStore().getState().userX[screenType])) {
- // userXId = DUMMY_USERID;
- // }
-
- /**
- * Code under useFocusEffect gets executed every time the screen comes under focus / is being viewed by the user.
- * This is done to reset the users stored in our store for the Search screen.
- * Read more about useFocusEffect here : https://reactnavigation.org/docs/function-after-focusing-screen/
- */
- // useFocusEffect(() => {
- // if (!userXId) {
- // dispatch(resetScreenType(screenType));
- // }
- // });
return (
<>
<StatusBar barStyle="dark-content" />
- <Content {...{y, userXId, screenType}} />
+ <Content {...{userXId, screenType}} />
<TabsGradient />
</>
);
diff --git a/src/screens/profile/SettingsScreen.tsx b/src/screens/profile/SettingsScreen.tsx
index 05e051b5..ecc3bafd 100644
--- a/src/screens/profile/SettingsScreen.tsx
+++ b/src/screens/profile/SettingsScreen.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, {useContext} from 'react';
import {
SafeAreaView,
SectionList,
@@ -17,6 +17,7 @@ import {BackgroundGradientType} from '../../types';
import {normalize, SCREEN_HEIGHT} from '../../utils/layouts';
import SettingsCell from './SettingsCell';
import {useNavigation} from '@react-navigation/core';
+import {ChatContext} from '../../App';
const SettingsScreen: React.FC = () => {
const dispatch = useDispatch();
@@ -24,6 +25,7 @@ const SettingsScreen: React.FC = () => {
const {suggested_people_linked} = useSelector(
(state: RootState) => state.user.profile,
);
+ const {chatClient} = useContext(ChatContext);
return (
<>
@@ -49,7 +51,7 @@ const SettingsScreen: React.FC = () => {
<TouchableOpacity
style={styles.logoutContainerStyles}
onPress={() => {
- dispatch(logout());
+ dispatch(logout(chatClient));
navigation.reset({
index: 0,
routes: [{name: 'SuggestedPeople'}],
diff --git a/src/screens/search/DiscoverUsers.tsx b/src/screens/search/DiscoverUsers.tsx
index b87bfc37..f67585f2 100644
--- a/src/screens/search/DiscoverUsers.tsx
+++ b/src/screens/search/DiscoverUsers.tsx
@@ -126,7 +126,7 @@ const DiscoverUsers: React.FC<DiscoverUsersProps> = ({route}) => {
ListFooterComponent={() => (
<>
<Text style={styles.otherGroups}>Other Groups</Text>
- <SearchCategories darkStyle={true} />
+ <SearchCategories useSuggestions={true} darkStyle={true} />
</>
)}
/>
diff --git a/src/screens/search/SearchScreen.tsx b/src/screens/search/SearchScreen.tsx
index 4f0cabb4..f7e1c467 100644
--- a/src/screens/search/SearchScreen.tsx
+++ b/src/screens/search/SearchScreen.tsx
@@ -1,8 +1,19 @@
import AsyncStorage from '@react-native-community/async-storage';
import {useFocusEffect} from '@react-navigation/native';
import React, {useEffect, useState} from 'react';
-import {Keyboard, ScrollView, StatusBar, StyleSheet} from 'react-native';
-import Animated, {Easing, timing} from 'react-native-reanimated';
+import {
+ Keyboard,
+ StatusBar,
+ StyleSheet,
+ LayoutChangeEvent,
+ SectionListData,
+} from 'react-native';
+import {
+ useSharedValue,
+ withTiming,
+ Easing,
+ runOnJS,
+} from 'react-native-reanimated';
import {SafeAreaView} from 'react-native-safe-area-context';
import {useDispatch, useSelector} from 'react-redux';
import {
@@ -13,22 +24,14 @@ import {
SearchResultsBackground,
TabsGradient,
} from '../../components';
-import {SEARCH_ENDPOINT, TAGG_LIGHT_BLUE} from '../../constants';
+import {SEARCH_ENDPOINT} from '../../constants';
import {loadSearchResults} from '../../services';
import {resetScreenType} from '../../store/actions';
import {RootState} from '../../store/rootReducer';
-import {
- CategoryPreviewType,
- ProfilePreviewType,
- ScreenType,
- SearchCategoryType,
-} from '../../types';
+import {CategoryPreviewType, ProfilePreviewType, ScreenType} from '../../types';
import {
getRecentlySearchedCategories,
getRecentlySearchedUsers,
- normalize,
- SCREEN_HEIGHT,
- SCREEN_WIDTH,
} from '../../utils';
/**
@@ -38,11 +41,8 @@ import {
const SearchScreen: React.FC = () => {
const {recentSearches} = useSelector((state: RootState) => state.taggUsers);
- const {
- profile: {university = ''},
- } = useSelector((state: RootState) => state.user);
const [query, setQuery] = useState<string>('');
- const [results, setResults] = useState<Array<any> | undefined>(undefined);
+ const [results, setResults] = useState<SectionListData<any>[] | undefined>();
const [recents, setRecents] = useState<Array<ProfilePreviewType>>(
recentSearches ?? [],
);
@@ -50,26 +50,12 @@ const SearchScreen: React.FC = () => {
CategoryPreviewType[]
>([]);
const [searching, setSearching] = useState(false);
- const top = Animated.useValue(-SCREEN_HEIGHT);
- const defaultButtons: SearchCategoryType[] = [21, 22, 23, 24].map((year) => ({
- id: -1,
- name: `${university.split(' ')[0]} '${year}`,
- category: university,
- }));
- const [keyboardVisible, setKeyboardVisible] = React.useState(
- 'keyboardVisible',
- );
- useEffect(() => {
- const showKeyboard = () => setKeyboardVisible('keyboardVisibleTrue');
- Keyboard.addListener('keyboardWillShow', showKeyboard);
- return () => Keyboard.removeListener('keyboardWillShow', showKeyboard);
- }, []);
+ /*
+ * Animated value
+ */
+ const animationProgress = useSharedValue<number>(0);
+ const [searchBarHeight, setSearchBarHeight] = useState<number>(0);
- useEffect(() => {
- const hideKeyboard = () => setKeyboardVisible('keyboardVisibleFalse');
- Keyboard.addListener('keyboardWillHide', hideKeyboard);
- return () => Keyboard.removeListener('keyboardWillHide', hideKeyboard);
- }, []);
const dispatch = useDispatch();
/*
@@ -122,12 +108,22 @@ const SearchScreen: React.FC = () => {
useEffect(() => {
if (searching) {
loadRecentlySearched().then(() => {
- timing(top, topInConfig).start();
+ animationProgress.value = withTiming(1, {
+ duration: 180,
+ easing: Easing.bezier(0.31, 0.14, 0.66, 0.82),
+ });
});
} else {
setQuery('');
handleBlur();
- timing(top, topOutConfig).start(() => setResults(undefined));
+ animationProgress.value = withTiming(
+ 0,
+ {duration: 180, easing: Easing.inOut(Easing.ease)},
+ () => {
+ 'worklet';
+ runOnJS(setResults)(undefined);
+ },
+ );
}
}, [searching]);
@@ -153,16 +149,6 @@ const SearchScreen: React.FC = () => {
}
};
- const topInConfig = {
- duration: 180,
- toValue: 0,
- easing: Easing.bezier(0.31, 0.14, 0.66, 0.82),
- };
- const topOutConfig = {
- duration: 180,
- toValue: -SCREEN_HEIGHT,
- easing: Easing.inOut(Easing.ease),
- };
const handleFocus = () => {
setSearching(true);
};
@@ -172,9 +158,12 @@ const SearchScreen: React.FC = () => {
const handleCancel = () => {
setSearching(false);
};
+ const onSearchBarLayout = (e: LayoutChangeEvent) => {
+ setSearchBarHeight(e.nativeEvent.layout.height);
+ };
return (
- <SafeAreaView style={styles.screenContainer}>
+ <SafeAreaView style={styles.container}>
<StatusBar barStyle="dark-content" />
<SearchBar
onCancel={handleCancel}
@@ -182,98 +171,39 @@ const SearchScreen: React.FC = () => {
onBlur={handleBlur}
onFocus={handleFocus}
value={query}
- {...{top, searching}}
+ onLayout={onSearchBarLayout}
+ {...{animationProgress, searching}}
/>
- <ScrollView
- scrollEnabled={!searching}
- keyboardShouldPersistTaps={'always'}
- stickyHeaderIndices={[4]}
- contentContainerStyle={styles.contentContainer}
- showsVerticalScrollIndicator={false}>
- <SearchCategories defaultButtons={defaultButtons} />
- <SearchResultsBackground {...{top}}>
- {results === undefined &&
- recents.length + recentCategories.length !== 0 ? (
+ <SearchCategories useSuggestions={false} />
+ <SearchResultsBackground
+ {...{searching, searchBarHeight, animationProgress}}>
+ {results === undefined ? (
+ recents.length + recentCategories.length > 0 && (
<RecentSearches
sectionTitle="Recent"
onPress={clearRecentlySearched}
screenType={ScreenType.Search}
{...{recents, recentCategories}}
/>
- ) : (
- <SearchResultList
- {...{results}}
- keyboardVisible={keyboardVisible === 'keyboardVisibleTrue'}
- previewType={'Search'}
- screenType={ScreenType.Search}
- />
- )}
- </SearchResultsBackground>
- </ScrollView>
+ )
+ ) : (
+ <SearchResultList
+ {...{results}}
+ previewType={'Search'}
+ screenType={ScreenType.Search}
+ />
+ )}
+ </SearchResultsBackground>
<TabsGradient />
</SafeAreaView>
);
};
const styles = StyleSheet.create({
- screenContainer: {
+ container: {
+ flex: 1,
paddingTop: 15,
backgroundColor: '#fff',
},
- contentContainer: {
- height: SCREEN_HEIGHT,
- paddingTop: '2%',
- paddingBottom: SCREEN_HEIGHT / 3,
- paddingHorizontal: '3%',
- },
- header: {
- marginVertical: 20,
- zIndex: 1,
- },
- recentsHeaderContainer: {
- flexDirection: 'row',
- },
- recentsHeader: {
- fontSize: 17,
- fontWeight: 'bold',
- flexGrow: 1,
- },
- clear: {
- fontSize: normalize(17),
- fontWeight: 'bold',
- color: TAGG_LIGHT_BLUE,
- },
- image: {
- width: SCREEN_WIDTH,
- height: SCREEN_WIDTH,
- },
- textContainer: {
- marginTop: '10%',
- },
- headerText: {
- color: '#fff',
- fontSize: normalize(32),
- fontWeight: '600',
- textAlign: 'center',
- marginBottom: '4%',
- marginHorizontal: '10%',
- },
- subtext: {
- color: '#fff',
- fontSize: normalize(16),
- fontWeight: '600',
- textAlign: 'center',
- marginHorizontal: '10%',
- },
- cancelButton: {
- position: 'absolute',
- height: '100%',
- justifyContent: 'center',
- paddingHorizontal: 5,
- },
- cancelText: {
- color: '#818181',
- fontWeight: '600',
- },
});
export default SearchScreen;
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)}