aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIvan Chen <ivan@tagg.id>2021-04-09 19:49:27 -0400
committerGitHub <noreply@github.com>2021-04-09 19:49:27 -0400
commit9d5ad9bea36c0b2abffd04b25126d18158017137 (patch)
tree7d7c58cbdb67e3e0945a6ed2ccaa658f0048e887 /src
parenta6580f418e87775a5e683e56735952df4f7fa5a9 (diff)
parentad9db5ffd0b2fb6334fd8237e0600a76d25c7053 (diff)
Merge pull request #356 from shravyaramesh/tma767-message-button
[TMA767] Message button
Diffstat (limited to 'src')
-rw-r--r--src/components/common/AcceptDeclineButtons.tsx2
-rw-r--r--src/components/common/BasicButton.tsx77
-rw-r--r--src/components/common/FriendsButton.tsx2
-rw-r--r--src/components/common/index.ts1
-rw-r--r--src/components/messages/ChatHeader.tsx81
-rw-r--r--src/components/messages/MessagesHeader.tsx29
-rw-r--r--src/components/profile/Content.tsx11
-rw-r--r--src/components/profile/Friends.tsx4
-rw-r--r--src/components/profile/ProfileBody.tsx99
-rw-r--r--src/components/profile/ToggleButton.tsx12
-rw-r--r--src/constants/api.ts4
-rw-r--r--src/constants/strings.ts2
-rw-r--r--src/routes/Routes.tsx15
-rw-r--r--src/screens/chat/ChatListScreen.tsx83
-rw-r--r--src/screens/chat/ChatScreen.tsx3
-rw-r--r--src/screens/onboarding/CategorySelection.tsx4
-rw-r--r--src/screens/onboarding/InvitationCodeVerification.tsx4
-rw-r--r--src/screens/onboarding/Login.tsx8
-rw-r--r--src/services/ChatService.ts22
-rw-r--r--src/store/actions/user.ts1
-rw-r--r--src/store/initialStates.ts1
-rw-r--r--src/store/reducers/userReducer.ts1
-rw-r--r--src/utils/common.ts18
-rw-r--r--src/utils/index.ts1
-rw-r--r--src/utils/messages.ts59
-rw-r--r--src/utils/users.ts2
26 files changed, 417 insertions, 129 deletions
diff --git a/src/components/common/AcceptDeclineButtons.tsx b/src/components/common/AcceptDeclineButtons.tsx
index 167148f0..7bb62fd4 100644
--- a/src/components/common/AcceptDeclineButtons.tsx
+++ b/src/components/common/AcceptDeclineButtons.tsx
@@ -58,7 +58,7 @@ const styles = StyleSheet.create({
backgroundColor: TAGG_LIGHT_BLUE,
},
rejectButton: {
- borderWidth: 1,
+ borderWidth: 2,
backgroundColor: 'white',
borderColor: TAGG_LIGHT_BLUE,
},
diff --git a/src/components/common/BasicButton.tsx b/src/components/common/BasicButton.tsx
new file mode 100644
index 00000000..1fe29cd9
--- /dev/null
+++ b/src/components/common/BasicButton.tsx
@@ -0,0 +1,77 @@
+import React from 'react';
+import {StyleProp, StyleSheet, Text, View, ViewStyle} from 'react-native';
+import {TAGG_LIGHT_BLUE} from '../../constants';
+import {TouchableOpacity} from 'react-native-gesture-handler';
+import {normalize} from '../../utils';
+
+interface BasicButtonProps {
+ title: string;
+ onPress: () => void;
+ solid?: boolean;
+ externalStyles?: Record<string, StyleProp<ViewStyle>>;
+}
+const BasicButton: React.FC<BasicButtonProps> = ({
+ title,
+ onPress,
+ solid,
+ externalStyles,
+}) => {
+ return (
+ <View style={[styles.container, externalStyles?.container]}>
+ <TouchableOpacity
+ style={[
+ styles.genericButtonStyle,
+ solid ? styles.solidButton : styles.outlineButton,
+ ]}
+ onPress={onPress}>
+ <Text
+ style={[
+ styles.buttonTitle,
+ solid
+ ? styles.solidButtonTitleColor
+ : styles.outlineButtonTitleColor,
+ ]}>
+ {title}
+ </Text>
+ </TouchableOpacity>
+ </View>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ height: '100%',
+ flexDirection: 'column',
+ justifyContent: 'space-around',
+ },
+ genericButtonStyle: {
+ justifyContent: 'center',
+ alignItems: 'center',
+ borderRadius: 3,
+ padding: 0,
+ width: '100%',
+ height: '100%',
+ },
+ solidButton: {
+ padding: 0,
+ backgroundColor: TAGG_LIGHT_BLUE,
+ },
+ outlineButton: {
+ borderWidth: 2,
+ backgroundColor: 'white',
+ borderColor: TAGG_LIGHT_BLUE,
+ },
+ solidButtonTitleColor: {
+ color: 'white',
+ },
+ outlineButtonTitleColor: {
+ color: TAGG_LIGHT_BLUE,
+ },
+ buttonTitle: {
+ fontSize: normalize(15),
+ fontWeight: '700',
+ letterSpacing: 1,
+ },
+});
+
+export default BasicButton;
diff --git a/src/components/common/FriendsButton.tsx b/src/components/common/FriendsButton.tsx
index 46421bd1..6ddad93f 100644
--- a/src/components/common/FriendsButton.tsx
+++ b/src/components/common/FriendsButton.tsx
@@ -100,7 +100,7 @@ const styles = StyleSheet.create({
button: {
justifyContent: 'center',
alignItems: 'center',
- width: SCREEN_WIDTH * 0.4,
+ width: SCREEN_WIDTH * 0.42,
aspectRatio: 154 / 33,
borderWidth: 2,
borderColor: TAGG_LIGHT_BLUE,
diff --git a/src/components/common/index.ts b/src/components/common/index.ts
index 8499dbfa..b5fd0542 100644
--- a/src/components/common/index.ts
+++ b/src/components/common/index.ts
@@ -23,3 +23,4 @@ export {default as AcceptDeclineButtons} from './AcceptDeclineButtons';
export {default as FriendsButton} from './FriendsButton';
export {default as TaggSquareButton} from './TaggSquareButton';
export {default as GradientBorderButton} from './GradientBorderButton';
+export {default as BasicButton} from './BasicButton';
diff --git a/src/components/messages/ChatHeader.tsx b/src/components/messages/ChatHeader.tsx
index 2bc096ec..67a7f1fe 100644
--- a/src/components/messages/ChatHeader.tsx
+++ b/src/components/messages/ChatHeader.tsx
@@ -1,39 +1,71 @@
+import {useNavigation} from '@react-navigation/native';
import React, {useContext} from 'react';
import {Image, StyleSheet, View} from 'react-native';
import {Text} from 'react-native-animatable';
-import {useStore} from 'react-redux';
+import {TouchableOpacity} from 'react-native-gesture-handler';
+import {useDispatch, useStore} from 'react-redux';
import {ChatContext} from '../../App';
-import {ChatHeaderHeight, normalize, StatusBarHeight} from '../../utils';
+import {ScreenType} from '../../types';
+import {
+ ChatHeaderHeight,
+ fetchUserX,
+ normalize,
+ StatusBarHeight,
+ userXInStore,
+} from '../../utils';
import {formatLastSeenText, getMember, isOnline} from '../../utils/messages';
-type ChatHeaderProps = {};
+type ChatHeaderProps = {
+ screenType: ScreenType;
+};
-const ChatHeader: React.FC<ChatHeaderProps> = () => {
+const ChatHeader: React.FC<ChatHeaderProps> = (props) => {
+ const {screenType} = props;
const {channel} = useContext(ChatContext);
+ const dispatch = useDispatch();
+ const navigation = useNavigation();
const state = useStore().getState();
const member = getMember(channel, state);
const online = isOnline(member?.user?.last_active);
const lastSeen = formatLastSeenText(member?.user?.last_active);
+ const toProfile = async () => {
+ if (member && member.user && member.user.username) {
+ if (!userXInStore(state, screenType, member.user.id)) {
+ await fetchUserX(
+ dispatch,
+ {userId: member.user.id, username: member.user.username},
+ screenType,
+ );
+ }
+ navigation.navigate('Profile', {
+ userXId: member.user.id,
+ screenType,
+ });
+ }
+ };
+
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>
+ <TouchableOpacity style={styles.tappables} onPress={toProfile}>
+ <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>
+ </TouchableOpacity>
</View>
);
};
@@ -41,10 +73,13 @@ const ChatHeader: React.FC<ChatHeaderProps> = () => {
const styles = StyleSheet.create({
container: {
height: ChatHeaderHeight - StatusBarHeight,
- flexDirection: 'row',
- alignItems: 'center',
paddingLeft: '15%',
},
+ tappables: {
+ alignItems: 'center',
+ flexDirection: 'row',
+ width: '100%',
+ },
avatar: {
width: normalize(40),
height: normalize(40),
diff --git a/src/components/messages/MessagesHeader.tsx b/src/components/messages/MessagesHeader.tsx
index 660da97d..1bd9b55a 100644
--- a/src/components/messages/MessagesHeader.tsx
+++ b/src/components/messages/MessagesHeader.tsx
@@ -1,10 +1,10 @@
-import React, {Fragment, useContext} from 'react';
+import React, {Fragment, useContext, useEffect, useState} 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';
+import ComposeIcon from '../../assets/icons/compose.svg';
+import {normalize} from '../../utils';
type MessagesHeaderProps = {
createChannel: () => void;
@@ -12,11 +12,30 @@ type MessagesHeaderProps = {
const MessagesHeader: React.FC<MessagesHeaderProps> = ({createChannel}) => {
const {chatClient} = useContext(ChatContext);
- const unread = chatClient.user?.total_unread_count as number;
+ const [unread, setUnread] = useState(0);
+
+ useEffect(() => {
+ const newCount = chatClient.user?.total_unread_count as number;
+ if (newCount) {
+ setUnread(newCount);
+ }
+ const listener = chatClient?.on((e) => {
+ if (e.total_unread_count) {
+ setUnread(e.total_unread_count);
+ }
+ });
+
+ return () => {
+ if (listener) {
+ listener.unsubscribe();
+ }
+ };
+ }, [chatClient]);
+
return (
<View style={styles.header}>
<Text style={styles.headerText}>Messages</Text>
- {unread && unread !== 0 ? (
+ {unread !== 0 ? (
<Text style={styles.unreadText}>
{unread > 99 ? '99+' : unread} unread
</Text>
diff --git a/src/components/profile/Content.tsx b/src/components/profile/Content.tsx
index 05098d14..0052b61d 100644
--- a/src/components/profile/Content.tsx
+++ b/src/components/profile/Content.tsx
@@ -1,4 +1,10 @@
-import React, {useCallback, useEffect, useRef, useState} from 'react';
+import React, {
+ useCallback,
+ useContext,
+ useEffect,
+ useRef,
+ useState,
+} from 'react';
import {LayoutChangeEvent, RefreshControl, StyleSheet} from 'react-native';
import Animated, {
useSharedValue,
@@ -31,6 +37,7 @@ import ProfileCutout from './ProfileCutout';
import ProfileHeader from './ProfileHeader';
import PublicProfile from './PublicProfile';
import {useScrollToTop} from '@react-navigation/native';
+import {ChatContext} from '../../App';
interface ContentProps {
userXId: string | undefined;
@@ -52,6 +59,8 @@ const Content: React.FC<ContentProps> = ({userXId, screenType}) => {
);
const state: RootState = useStore().getState();
+ const {chatClient} = useContext(ChatContext);
+
/*
* Used to imperatively scroll to the top when presenting the moment tutorial.
*/
diff --git a/src/components/profile/Friends.tsx b/src/components/profile/Friends.tsx
index c1dca755..b754b71a 100644
--- a/src/components/profile/Friends.tsx
+++ b/src/components/profile/Friends.tsx
@@ -191,7 +191,7 @@ const styles = StyleSheet.create({
height: '55%',
borderColor: TAGG_LIGHT_BLUE,
borderWidth: 2,
- borderRadius: 2,
+ borderRadius: 3,
padding: 0,
backgroundColor: TAGG_LIGHT_BLUE,
},
@@ -212,7 +212,7 @@ const styles = StyleSheet.create({
height: '55%',
borderColor: TAGG_LIGHT_BLUE,
borderWidth: 2,
- borderRadius: 2,
+ borderRadius: 3,
padding: 0,
},
unfriendButtonTitle: {
diff --git a/src/components/profile/ProfileBody.tsx b/src/components/profile/ProfileBody.tsx
index b49e71a3..527036f6 100644
--- a/src/components/profile/ProfileBody.tsx
+++ b/src/components/profile/ProfileBody.tsx
@@ -1,5 +1,12 @@
-import React from 'react';
-import {LayoutChangeEvent, Linking, StyleSheet, Text, View} from 'react-native';
+import React, {useContext} from 'react';
+import {
+ Alert,
+ LayoutChangeEvent,
+ Linking,
+ StyleSheet,
+ Text,
+ View,
+} from 'react-native';
import {normalize} from 'react-native-elements';
import {useDispatch, useSelector, useStore} from 'react-redux';
import {TAGG_DARK_BLUE, TOGGLE_BUTTON_TYPE} from '../../constants';
@@ -9,12 +16,23 @@ import {
updateUserXFriends,
updateUserXProfileAllScreens,
} from '../../store/actions';
+import {canViewProfile} from '../../utils/users';
import {NO_PROFILE} from '../../store/initialStates';
import {RootState} from '../../store/rootReducer';
import {ScreenType} from '../../types';
-import {getUserAsProfilePreviewType} from '../../utils';
-import {FriendsButton} from '../common';
+import {
+ connectChatAccount,
+ createChannel,
+ getUserAsProfilePreviewType,
+ SCREEN_HEIGHT,
+ SCREEN_WIDTH,
+} from '../../utils';
+import {FriendsButton, BasicButton} from '../common';
import ToggleButton from './ToggleButton';
+import {ChatContext} from '../../App';
+import {useNavigation} from '@react-navigation/core';
+import {ChatListScreen} from '../../screens';
+import {ERROR_UNABLE_CONNECT_CHAT} from '../../constants/strings';
interface ProfileBodyProps {
onLayout: (event: LayoutChangeEvent) => void;
@@ -30,6 +48,9 @@ const ProfileBody: React.FC<ProfileBodyProps> = ({
userXId,
screenType,
}) => {
+ const dispatch = useDispatch();
+ const navigation = useNavigation();
+
const {profile = NO_PROFILE, user} = useSelector((state: RootState) =>
userXId ? state.userX[screenType][userXId] : state.user,
);
@@ -46,8 +67,10 @@ const ProfileBody: React.FC<ProfileBodyProps> = ({
profile,
);
+ const {chatClient, setChannel} = useContext(ChatContext);
+
const state: RootState = useStore().getState();
- const dispatch = useDispatch();
+ const loggedInUserId = state.user.user.userId;
const handleAcceptRequest = async () => {
await dispatch(acceptFriendRequest({id, username, first_name, last_name}));
@@ -60,6 +83,32 @@ const ProfileBody: React.FC<ProfileBodyProps> = ({
dispatch(updateUserXProfileAllScreens(id, state));
};
+ const canMessage = () => {
+ if (
+ userXId &&
+ !isBlocked &&
+ (friendship_status === 'no_record' ||
+ friendship_status === 'friends' ||
+ (friendship_status === 'requested' &&
+ friendship_requester_id === loggedInUserId)) &&
+ canViewProfile(state, userXId, screenType)
+ ) {
+ return true;
+ } else {
+ return false;
+ }
+ };
+
+ const onPressMessage = async () => {
+ if (chatClient.user && userXId) {
+ const channel = await createChannel(loggedInUserId, userXId, chatClient);
+ setChannel(channel);
+ navigation.navigate('Chat');
+ } else {
+ Alert.alert(ERROR_UNABLE_CONNECT_CHAT);
+ }
+ };
+
return (
<View onLayout={onLayout} style={styles.container}>
<Text style={styles.username}>{`@${username}`}</Text>
@@ -85,17 +134,31 @@ const ProfileBody: React.FC<ProfileBodyProps> = ({
/>
</View>
)}
- {userXId && !isBlocked && (
- <View style={styles.buttonsContainer}>
- <FriendsButton
- userXId={userXId}
- screenType={screenType}
- friendship_requester_id={friendship_requester_id}
- onAcceptRequest={handleAcceptRequest}
- onRejectRequest={handleDeclineFriendRequest}
- />
- </View>
- )}
+ <View style={styles.simpleRowContainer}>
+ {userXId && !isBlocked && (
+ <View style={styles.buttonsContainer}>
+ <FriendsButton
+ userXId={userXId}
+ screenType={screenType}
+ friendship_requester_id={friendship_requester_id}
+ onAcceptRequest={handleAcceptRequest}
+ onRejectRequest={handleDeclineFriendRequest}
+ />
+ {canMessage() && (
+ <BasicButton
+ title={'Message'}
+ onPress={onPressMessage}
+ externalStyles={{
+ container: {
+ width: SCREEN_WIDTH * 0.42,
+ aspectRatio: 154 / 33,
+ },
+ }}
+ />
+ )}
+ </View>
+ )}
+ </View>
</View>
);
};
@@ -107,11 +170,15 @@ const styles = StyleSheet.create({
paddingTop: '3.5%',
paddingBottom: '2%',
},
+ simpleRowContainer: {flexDirection: 'row'},
buttonsContainer: {
flex: 1,
paddingTop: '3.5%',
paddingBottom: '2%',
width: '50%',
+ height: SCREEN_HEIGHT * 0.1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
},
container: {
paddingVertical: '1%',
diff --git a/src/components/profile/ToggleButton.tsx b/src/components/profile/ToggleButton.tsx
index 236d811c..4697d744 100644
--- a/src/components/profile/ToggleButton.tsx
+++ b/src/components/profile/ToggleButton.tsx
@@ -2,7 +2,7 @@ import * as React from 'react';
import {StyleSheet, Text} from 'react-native';
import {TouchableOpacity} from 'react-native-gesture-handler';
import {TAGG_LIGHT_BLUE} from '../../constants';
-import {getToggleButtonText, SCREEN_WIDTH} from '../../utils';
+import {getToggleButtonText, normalize, SCREEN_WIDTH} from '../../utils';
type ToggleButtonProps = {
toggleState: boolean;
@@ -34,15 +34,17 @@ const styles = StyleSheet.create({
button: {
justifyContent: 'center',
alignItems: 'center',
- width: SCREEN_WIDTH * 0.4,
+ width: SCREEN_WIDTH * 0.42,
height: SCREEN_WIDTH * 0.08,
borderColor: TAGG_LIGHT_BLUE,
- borderWidth: 3,
- borderRadius: 5,
+ borderWidth: 2,
+ borderRadius: 3,
marginRight: '2%',
},
text: {
- fontWeight: 'bold',
+ fontWeight: '700',
+ fontSize: normalize(15),
+ letterSpacing: 1,
},
buttonColor: {
backgroundColor: TAGG_LIGHT_BLUE,
diff --git a/src/constants/api.ts b/src/constants/api.ts
index 43294386..cb45b238 100644
--- a/src/constants/api.ts
+++ b/src/constants/api.ts
@@ -61,6 +61,10 @@ export const UPDATE_BADGES_ENDPOINT: string =
// Register as FCM device
export const FCM_ENDPOINT: string = API_URL + 'fcm/';
+// Retrieve Stream Chat token
+export const CHAT_ENDPOINT: string = API_URL + 'chat/';
+export const CHAT_TOKEN_ENDPOINT: string = CHAT_ENDPOINT + 'get_token/';
+
// Register Social Link (Non-integrated)
export const LINK_SNAPCHAT_ENDPOINT: string = API_URL + 'link-sc/';
export const LINK_TIKTOK_ENDPOINT: string = API_URL + 'link-tt/';
diff --git a/src/constants/strings.ts b/src/constants/strings.ts
index 300ceb90..bdb94fba 100644
--- a/src/constants/strings.ts
+++ b/src/constants/strings.ts
@@ -9,6 +9,7 @@ export const ERROR_AUTHENTICATION = 'An error occurred during authentication. Pl
export const ERROR_BADGES_EXCEED_LIMIT = 'You can\'t have more than 5 badges!';
export const ERROR_CATEGORY_CREATION = 'There was a problem creating your categories. Please refresh and try again.';
export const ERROR_CATEGORY_UPDATE = 'There was a problem updating your categories. Please refresh and try again';
+export const ERROR_CHAT_CONNECTION = `Unable to establish chat connection`;
export const ERROR_DELETE_CATEGORY = 'There was a problem while deleting category. Please try again';
export const ERROR_DELETE_MOMENT = 'Unable to delete moment, please try again later!';
export const ERROR_DELETED_OBJECT = 'Oh sad! Looks like the comment / moment was deleted by the user';
@@ -44,6 +45,7 @@ export const ERROR_SOMETHING_WENT_WRONG = 'Oh dear, don’t worry someone will b
export const ERROR_SOMETHING_WENT_WRONG_REFRESH = "Ha, looks like this one's on us, please refresh and try again";
export const ERROR_SOMETHING_WENT_WRONG_RELOAD = "You broke it, Just kidding! we don't know what happened... Please reload the app and try again";
export const ERROR_TWILIO_SERVER_ERROR = 'mhm, looks like that is an invalid phone number or our servers are down, please try again in a few mins';
+export const ERROR_UNABLE_CONNECT_CHAT = 'Unable to connect chat';
export const ERROR_UNABLE_TO_FIND_PROFILE = 'We were unable to find this profile. Please check username and try again';
export const ERROR_UNABLE_TO_VIEW_PROFILE = 'Unable to view this profile';
export const ERROR_UPLOAD = 'An error occurred while uploading. Please try again!';
diff --git a/src/routes/Routes.tsx b/src/routes/Routes.tsx
index 819ca785..5ce0c771 100644
--- a/src/routes/Routes.tsx
+++ b/src/routes/Routes.tsx
@@ -1,15 +1,17 @@
+import AsyncStorage from '@react-native-community/async-storage';
import messaging from '@react-native-firebase/messaging';
-import React, {useEffect, useState} from 'react';
+import React, {useContext, useEffect, useState} from 'react';
import DeviceInfo from 'react-native-device-info';
import SplashScreen from 'react-native-splash-screen';
-import {useDispatch, useSelector} from 'react-redux';
+import {useDispatch, useSelector, useStore} from 'react-redux';
+import {ChatContext} from '../App';
import {fcmService, getCurrentLiveVersions} from '../services';
import {
updateNewNotificationReceived,
updateNewVersionAvailable,
} from '../store/actions';
import {RootState} from '../store/rootReducer';
-import {userLogin} from '../utils';
+import {userLogin, connectChatAccount} from '../utils';
import Onboarding from './onboarding';
import NavigationBar from './tabs';
@@ -17,6 +19,9 @@ const Routes: React.FC = () => {
const {
user: {userId},
} = useSelector((state: RootState) => state.user);
+ const state: RootState = useStore().getState();
+ const loggedInUserId = state.user.user.userId;
+ const {chatClient} = useContext(ChatContext);
const [newVersionAvailable, setNewVersionAvailable] = useState(false);
const dispatch = useDispatch();
@@ -53,6 +58,10 @@ const Routes: React.FC = () => {
}, []);
useEffect(() => {
+ connectChatAccount(loggedInUserId, chatClient);
+ }, [loggedInUserId]);
+
+ useEffect(() => {
const checkVersion = async () => {
const liveVersions = await getCurrentLiveVersions();
if (liveVersions && !liveVersions.includes(DeviceInfo.getVersion())) {
diff --git a/src/screens/chat/ChatListScreen.tsx b/src/screens/chat/ChatListScreen.tsx
index daea9984..f9db0c77 100644
--- a/src/screens/chat/ChatListScreen.tsx
+++ b/src/screens/chat/ChatListScreen.tsx
@@ -1,7 +1,6 @@
-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 {Alert, SafeAreaView, StatusBar, StyleSheet, View} from 'react-native';
import {useStore} from 'react-redux';
import {ChannelList, Chat} from 'stream-chat-react-native';
import {ChatContext} from '../../App';
@@ -18,8 +17,9 @@ import {
LocalReactionType,
LocalUserType,
} from '../../types';
-
+import {connectChatAccount} from '../../utils';
import NewChatModal from './NewChatModal';
+
type ChatListScreenNavigationProp = StackNavigationProp<
MainStackParams,
'ChatList'
@@ -30,11 +30,9 @@ interface ChatListScreenProps {
/*
* Screen that displays all of the user's active conversations.
*/
-const ChatListScreen: React.FC<ChatListScreenProps> = ({navigation}) => {
- const {chatClient, setChannel} = useContext(ChatContext);
+const ChatListScreen: React.FC<ChatListScreenProps> = () => {
+ const {chatClient} = useContext(ChatContext);
const [modalVisible, setChatModalVisible] = useState(false);
-
- const [clientReady, setClientReady] = useState(false);
const state: RootState = useStore().getState();
const loggedInUserId = state.user.user.userId;
@@ -47,21 +45,16 @@ const ChatListScreen: React.FC<ChatListScreenProps> = ({navigation}) => {
);
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);
+ connectChatAccount(loggedInUserId, chatClient)
+ .then((success) => {
+ if (!success) {
+ Alert.alert('Something wrong with chat');
+ }
+ })
+ .catch((err) => {
+ console.log('Error connecting to chat: ', err);
+ Alert.alert('Something wrong with chat');
});
- }
}, []);
return (
@@ -73,31 +66,29 @@ const ChatListScreen: React.FC<ChatListScreenProps> = ({navigation}) => {
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>
- )}
+ <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 />
diff --git a/src/screens/chat/ChatScreen.tsx b/src/screens/chat/ChatScreen.tsx
index 59c53c99..1554274d 100644
--- a/src/screens/chat/ChatScreen.tsx
+++ b/src/screens/chat/ChatScreen.tsx
@@ -12,6 +12,7 @@ import {
import {ChatContext} from '../../App';
import ChatHeader from '../../components/messages/ChatHeader';
import {MainStackParams} from '../../routes';
+import {ScreenType} from '../../types';
import {isIPhoneX} from '../../utils';
type ChatScreenNavigationProp = StackNavigationProp<MainStackParams, 'Chat'>;
@@ -32,7 +33,7 @@ const ChatScreen: React.FC<ChatScreenProps> = () => {
// unable to figure out the padding issue, a hacky solution
{paddingBottom: isIPhoneX() ? tabbarHeight + 20 : tabbarHeight + 50},
]}>
- <ChatHeader />
+ <ChatHeader screenType={ScreenType.Chat} />
<Chat client={chatClient}>
<Channel channel={channel} keyboardVerticalOffset={0}>
<MessageList onThreadSelect={() => {}} />
diff --git a/src/screens/onboarding/CategorySelection.tsx b/src/screens/onboarding/CategorySelection.tsx
index 9d5fbe4d..ab5ff3be 100644
--- a/src/screens/onboarding/CategorySelection.tsx
+++ b/src/screens/onboarding/CategorySelection.tsx
@@ -1,6 +1,6 @@
import {RouteProp} from '@react-navigation/native';
import {StackNavigationProp} from '@react-navigation/stack';
-import React, {useEffect, useState} from 'react';
+import React, {useContext, useEffect, useState} from 'react';
import {
Alert,
Platform,
@@ -12,6 +12,7 @@ import {
} from 'react-native';
import {ScrollView} from 'react-native-gesture-handler';
import {useDispatch, useSelector} from 'react-redux';
+import {ChatContext} from '../../App';
import PlusIcon from '../../assets/icons/plus_icon-01.svg';
import {Background, MomentCategory} from '../../components';
import {MOMENT_CATEGORIES} from '../../constants';
@@ -49,6 +50,7 @@ const CategorySelection: React.FC<CategorySelectionProps> = ({
* Same component to be used for category selection while onboarding and while on profile
*/
const {screenType, user} = route.params;
+ const {chatClient} = useContext(ChatContext);
const isOnBoarding: boolean =
screenType === CategorySelectionScreenType.Onboarding;
const {userId, username} = user;
diff --git a/src/screens/onboarding/InvitationCodeVerification.tsx b/src/screens/onboarding/InvitationCodeVerification.tsx
index e160b4b7..6bc0ac9d 100644
--- a/src/screens/onboarding/InvitationCodeVerification.tsx
+++ b/src/screens/onboarding/InvitationCodeVerification.tsx
@@ -1,7 +1,7 @@
import AsyncStorage from '@react-native-community/async-storage';
import {RouteProp} from '@react-navigation/native';
import {StackNavigationProp} from '@react-navigation/stack';
-import React from 'react';
+import React, {useContext} from 'react';
import {Alert, KeyboardAvoidingView, StyleSheet, View} from 'react-native';
import {Text} from 'react-native-animatable';
import {
@@ -27,6 +27,7 @@ import {
import {OnboardingStackParams} from '../../routes';
import {BackgroundGradientType} from '../../types';
import {SCREEN_WIDTH, userLogin} from '../../utils';
+import {ChatContext} from '../../App';
type InvitationCodeVerificationRouteProp = RouteProp<
OnboardingStackParams,
@@ -58,6 +59,7 @@ const InvitationCodeVerification: React.FC<InvitationCodeVerificationProps> = ({
setValue,
});
const dispatch = useDispatch();
+ const {chatClient} = useContext(ChatContext);
const handleInvitationCodeVerification = async () => {
if (value.length === 6) {
diff --git a/src/screens/onboarding/Login.tsx b/src/screens/onboarding/Login.tsx
index dd2bb2e4..6d9abf82 100644
--- a/src/screens/onboarding/Login.tsx
+++ b/src/screens/onboarding/Login.tsx
@@ -1,7 +1,7 @@
import AsyncStorage from '@react-native-community/async-storage';
import {RouteProp} from '@react-navigation/native';
import {StackNavigationProp} from '@react-navigation/stack';
-import React, {useEffect, useRef} from 'react';
+import React, {useContext, useEffect, useRef} from 'react';
import {
Alert,
Image,
@@ -14,6 +14,7 @@ import {
} from 'react-native';
import SplashScreen from 'react-native-splash-screen';
import {useDispatch, useSelector} from 'react-redux';
+import {ChatContext} from '../../App';
import {Background, TaggInput, TaggSquareButton} from '../../components';
import {LOGIN_ENDPOINT, usernameRegex} from '../../constants';
import {
@@ -28,7 +29,7 @@ import {OnboardingStackParams} from '../../routes/onboarding';
import {fcmService} from '../../services';
import {RootState} from '../../store/rootReducer';
import {BackgroundGradientType, UniversityType} from '../../types';
-import {normalize, userLogin} from '../../utils';
+import {connectChatAccount, normalize, userLogin} from '../../utils';
import UpdateRequired from './UpdateRequired';
type VerificationScreenRouteProp = RouteProp<OnboardingStackParams, 'Login'>;
@@ -47,6 +48,7 @@ interface LoginProps {
const Login: React.FC<LoginProps> = ({navigation}: LoginProps) => {
// ref for focusing on input fields
const inputRef = useRef();
+ const {chatClient} = useContext(ChatContext);
// login form state
const [form, setForm] = React.useState({
@@ -160,7 +162,6 @@ 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) {
@@ -168,6 +169,7 @@ const Login: React.FC<LoginProps> = ({navigation}: LoginProps) => {
try {
userLogin(dispatch, {userId: data.UserID, username});
fcmService.sendFcmTokenToServer();
+ connectChatAccount(data.UserID, chatClient);
} catch (err) {
Alert.alert(ERROR_INVALID_LOGIN);
}
diff --git a/src/services/ChatService.ts b/src/services/ChatService.ts
new file mode 100644
index 00000000..e9b1c284
--- /dev/null
+++ b/src/services/ChatService.ts
@@ -0,0 +1,22 @@
+import AsyncStorage from '@react-native-community/async-storage';
+import {CHAT_TOKEN_ENDPOINT} from '../constants/api';
+
+export const loadChatTokenService = async () => {
+ try {
+ const token = await AsyncStorage.getItem('token');
+ const response = await fetch(CHAT_TOKEN_ENDPOINT, {
+ method: 'GET',
+ headers: {
+ Authorization: 'Token ' + token,
+ },
+ });
+ const status = response.status;
+ if (status === 200) {
+ const data = await response.json();
+ return data.chatToken;
+ }
+ return '';
+ } catch (error) {
+ console.log('Error loading chat token in service');
+ }
+};
diff --git a/src/store/actions/user.ts b/src/store/actions/user.ts
index 3ebd4190..c7d0d5a7 100644
--- a/src/store/actions/user.ts
+++ b/src/store/actions/user.ts
@@ -233,3 +233,4 @@ export const suggestedPeopleAnimatedTutorialFinished = (
console.log('Error while updating suggested people linked state: ', error);
}
};
+
diff --git a/src/store/initialStates.ts b/src/store/initialStates.ts
index 02331eb6..7fd3ac5a 100644
--- a/src/store/initialStates.ts
+++ b/src/store/initialStates.ts
@@ -117,6 +117,7 @@ export const EMPTY_SCREEN_TO_USERS_LIST: Record<
[ScreenType.Search]: EMPTY_USERX_LIST,
[ScreenType.Notifications]: EMPTY_USERX_LIST,
[ScreenType.SuggestedPeople]: EMPTY_USERX_LIST,
+ [ScreenType.Chat]: EMPTY_USERX_LIST,
};
export const INITIAL_CATEGORIES_STATE = {
diff --git a/src/store/reducers/userReducer.ts b/src/store/reducers/userReducer.ts
index 9ff9ba01..a8789c1d 100644
--- a/src/store/reducers/userReducer.ts
+++ b/src/store/reducers/userReducer.ts
@@ -90,5 +90,6 @@ export const {
setReplyPosted,
setSuggestedPeopleImage,
clearHeaderAndProfileImages,
+ // setChatClientReady,
} = userDataSlice.actions;
export const userDataReducer = userDataSlice.reducer;
diff --git a/src/utils/common.ts b/src/utils/common.ts
index 0900a084..7ae36dc6 100644
--- a/src/utils/common.ts
+++ b/src/utils/common.ts
@@ -173,21 +173,3 @@ const _crestIcon = (university: UniversityType) => {
return require('../assets/images/bwbadges.png');
}
};
-
-export const createChannel = async (
- loggedInUser: string,
- id: string,
- chatClient: any,
-) => {
- console.log(loggedInUser, id, chatClient);
- try {
- const channel = chatClient.channel('messaging', {
- members: [loggedInUser, id],
- });
- await channel.watch();
- return channel;
- } catch (error) {
- console.log(error);
- throw error;
- }
-};
diff --git a/src/utils/index.ts b/src/utils/index.ts
index 739e6fb8..4ff9afac 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -4,3 +4,4 @@ export * from './common';
export * from './users';
export * from './friends';
export * from './search';
+export * from './messages';
diff --git a/src/utils/messages.ts b/src/utils/messages.ts
index d63f2b7a..dd29f317 100644
--- a/src/utils/messages.ts
+++ b/src/utils/messages.ts
@@ -1,6 +1,9 @@
+import AsyncStorage from '@react-native-community/async-storage';
import moment from 'moment';
+import {loadChatTokenService} from '../services/ChatService';
import {RootState} from '../store/rootReducer';
import {ChannelGroupedType} from '../types';
+import {StreamChat} from 'stream-chat';
/**
* Finds the difference in time in minutes
@@ -81,3 +84,59 @@ export const getMember = (
: [];
return otherMembers.length === 1 ? otherMembers[0] : undefined;
};
+
+export const connectChatAccount = async (
+ loggedInUserId: string,
+ chatClient: StreamChat,
+) => {
+ try {
+ await getChatToken();
+ const chatToken = await AsyncStorage.getItem('chatToken');
+ if (!chatClient.user && chatToken) {
+ await chatClient.connectUser(
+ {
+ id: loggedInUserId,
+ },
+ chatToken,
+ );
+ return true;
+ } else if (chatClient.user) {
+ return true;
+ } else {
+ console.log('Unable to connect to stream. Empty chat token');
+ return false;
+ }
+ } catch (err) {
+ console.log('Error while connecting user to Stream: ', err);
+ return false;
+ }
+};
+
+export const getChatToken = async () => {
+ try {
+ const currentChatToken = await AsyncStorage.getItem('chatToken');
+ if (currentChatToken === null) {
+ const chatToken = await loadChatTokenService();
+ await AsyncStorage.setItem('chatToken', chatToken);
+ }
+ } catch (err) {
+ console.log(err);
+ }
+};
+
+export const createChannel = async (
+ loggedInUser: string,
+ id: string,
+ chatClient: any,
+) => {
+ try {
+ const channel = chatClient.channel('messaging', {
+ members: [loggedInUser, id],
+ });
+ await channel.watch();
+ return channel;
+ } catch (error) {
+ console.log(error);
+ throw error;
+ }
+};
diff --git a/src/utils/users.ts b/src/utils/users.ts
index 22c1c1f0..7148eb79 100644
--- a/src/utils/users.ts
+++ b/src/utils/users.ts
@@ -12,14 +12,12 @@ import {
logout,
} from '../store/actions';
import {NO_SOCIAL_ACCOUNTS} from '../store/initialStates';
-import {userLoggedIn} from '../store/reducers';
import {loadUserMomentCategories} from './../store/actions/momentCategories';
import {loadUserX} from './../store/actions/userX';
import {AppDispatch} from './../store/configureStore';
import {RootState} from './../store/rootReducer';
import {
ProfilePreviewType,
- CategoryPreviewType,
ProfileInfoType,
ScreenType,
UserType,