diff options
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/messages/ChannelPreview.tsx | 131 | ||||
| -rw-r--r-- | src/components/messages/ChatHeader.tsx | 84 | ||||
| -rw-r--r-- | src/components/messages/MessagesHeader.tsx | 20 | ||||
| -rw-r--r-- | src/components/messages/index.ts | 1 | ||||
| -rw-r--r-- | src/components/profile/Content.tsx | 13 | ||||
| -rw-r--r-- | src/components/profile/ProfileBody.tsx | 48 |
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 ( |
