aboutsummaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-rw-r--r--src/components/messages/ChannelPreview.tsx131
-rw-r--r--src/components/messages/ChatHeader.tsx84
-rw-r--r--src/components/messages/MessagesHeader.tsx20
-rw-r--r--src/components/messages/index.ts1
-rw-r--r--src/components/profile/Content.tsx13
-rw-r--r--src/components/profile/ProfileBody.tsx48
6 files changed, 266 insertions, 31 deletions
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
index d8445580..660da97d 100644
--- a/src/components/messages/MessagesHeader.tsx
+++ b/src/components/messages/MessagesHeader.tsx
@@ -1,23 +1,31 @@
-import * as React from 'react';
+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>
- <Text style={styles.unreadText}>2 unread</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}>
- <Text>Compose</Text>
+ <TouchableOpacity style={styles.compose} onPress={createChannel}>
+ <ComposeIcon width={normalize(20)} height={normalize(20)} />
</TouchableOpacity>
</View>
);
diff --git a/src/components/messages/index.ts b/src/components/messages/index.ts
index 2d6bb581..e194093c 100644
--- a/src/components/messages/index.ts
+++ b/src/components/messages/index.ts
@@ -1 +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 05098d14..0d2a0331 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.
*/
@@ -75,7 +84,7 @@ const Content: React.FC<ContentProps> = ({userXId, screenType}) => {
const refrestState = async () => {
setRefreshing(true);
if (!userXId) {
- await userLogin(dispatch, loggedInUser);
+ await userLogin(dispatch, loggedInUser, chatClient);
} else {
await fetchUserX(dispatch, user, screenType);
}
diff --git a/src/components/profile/ProfileBody.tsx b/src/components/profile/ProfileBody.tsx
index 1a8d1e1a..dc68446b 100644
--- a/src/components/profile/ProfileBody.tsx
+++ b/src/components/profile/ProfileBody.tsx
@@ -1,5 +1,12 @@
import React, {useContext} from 'react';
-import {LayoutChangeEvent, Linking, StyleSheet, Text, View} from 'react-native';
+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';
@@ -20,9 +27,8 @@ import {
} from '../../utils';
import {FriendsButton, BasicButton} from '../common';
import ToggleButton from './ToggleButton';
-// import {ChatContext} from '../../App';
-// import {useNavigation} from '@react-navigation/core';
-// import AsyncStorage from '@react-native-community/async-storage';
+import {ChatContext} from '../../App';
+import {useNavigation} from '@react-navigation/core';
interface ProfileBodyProps {
onLayout: (event: LayoutChangeEvent) => void;
@@ -38,6 +44,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,
);
@@ -54,10 +63,10 @@ const ProfileBody: React.FC<ProfileBodyProps> = ({
profile,
);
+ const {chatClientReady} = useSelector((state: RootState) => state.user);
+ const {chatClient, setChannel} = useContext(ChatContext);
+
const state: RootState = useStore().getState();
- const dispatch = useDispatch();
- // const navigation = useNavigation();
- // const {chatClient, setChannel} = useContext(ChatContext);
const loggedInUserId = state.user.user.userId;
const handleAcceptRequest = async () => {
@@ -88,22 +97,15 @@ const ProfileBody: React.FC<ProfileBodyProps> = ({
};
const onPressMessage = async () => {
- // TODO: Use function from util to create the channel and then navigate to screen
- // const channelName = username + ' and ' + state.user.user.username;
- // const chatToken = await AsyncStorage.getItem('chatToken');
- // await chatClient.connectUser(
- // {
- // id: loggedInUserId,
- // },
- // chatToken,
- // );
- // const channel = chatClient.channel('messaging', {
- // name: channelName,
- // members: [loggedInUserId, String(userXId)],
- // });
- // channel.create();
- // navigation.navigate('Chat');
- console.log('Navigate to ChatScreen');
+ if (!chatClientReady) {
+ Alert.alert('Something wrong with chat');
+ }
+ const channel = chatClient.channel('messaging', {
+ members: [loggedInUserId, String(userXId)],
+ });
+ channel.create();
+ setChannel(channel);
+ navigation.navigate('Chat');
};
return (