diff options
author | Ivan Chen <ivan@tagg.id> | 2021-04-13 19:53:07 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-13 19:53:07 -0400 |
commit | 3cc0e2e342993ce01490a36a43d99f2ab61ccb4f (patch) | |
tree | b57b61c49571c9e4b6d34c67f2f99c9617ff9d93 | |
parent | 93837e23a169e6b17432e4e0dd397bcaa140bb5e (diff) | |
parent | 3db3ba5fc35930cea99b2a51042446c0c470af47 (diff) |
Merge pull request #362 from IvanIFChen/tma784-message-input-camera
[TMA-784] Message input camera
-rw-r--r-- | src/App.tsx | 9 | ||||
-rw-r--r-- | src/assets/images/camera.png | bin | 0 -> 99285 bytes | |||
-rw-r--r-- | src/assets/images/gif.png | bin | 0 -> 103397 bytes | |||
-rw-r--r-- | src/components/messages/ChatInput.tsx | 101 | ||||
-rw-r--r-- | src/components/messages/ChatInputSubmit.tsx | 49 | ||||
-rw-r--r-- | src/components/messages/index.ts | 2 | ||||
-rw-r--r-- | src/components/profile/Content.tsx | 16 | ||||
-rw-r--r-- | src/screens/chat/ChatScreen.tsx | 161 |
8 files changed, 155 insertions, 183 deletions
diff --git a/src/App.tsx b/src/App.tsx index b8d64461..8d823e1f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -18,6 +18,7 @@ import { LocalResponseType, LocalUserType, } from './types'; +import {isIPhoneX} from './utils'; export const ChatContext = React.createContext({} as ChatContextType); @@ -38,11 +39,11 @@ const App = () => { */ <Provider store={store}> <NavigationContainer ref={navigationRef}> - <ChatContext.Provider value={{channel, setChannel, chatClient}}> - <OverlayProvider> + <OverlayProvider bottomInset={isIPhoneX() ? 80 : 50}> + <ChatContext.Provider value={{channel, setChannel, chatClient}}> <Routes /> - </OverlayProvider> - </ChatContext.Provider> + </ChatContext.Provider> + </OverlayProvider> </NavigationContainer> </Provider> ); diff --git a/src/assets/images/camera.png b/src/assets/images/camera.png Binary files differnew file mode 100644 index 00000000..16dbeb3a --- /dev/null +++ b/src/assets/images/camera.png diff --git a/src/assets/images/gif.png b/src/assets/images/gif.png Binary files differnew file mode 100644 index 00000000..f3d11a0c --- /dev/null +++ b/src/assets/images/gif.png diff --git a/src/components/messages/ChatInput.tsx b/src/components/messages/ChatInput.tsx index 9aeb9c62..005d4401 100644 --- a/src/components/messages/ChatInput.tsx +++ b/src/components/messages/ChatInput.tsx @@ -1,18 +1,14 @@ import React from 'react'; -import { - Image, - StyleSheet, - TextInput, - TouchableOpacity, - View, -} from 'react-native'; +import {Image, StyleSheet, View} from 'react-native'; +import {TouchableOpacity} from 'react-native-gesture-handler'; +import ImagePicker from 'react-native-image-crop-picker'; import {useStore} from 'react-redux'; import { + AutoCompleteInput, MessageInputProps, + useAttachmentPickerContext, useMessageInputContext, } from 'stream-chat-react-native'; -import UpArrowIcon from '../../assets/icons/up_arrow.svg'; -import {TAGG_LIGHT_BLUE} from '../../constants'; import {RootState} from '../../store/rootReducer'; import { LocalAttachmentType, @@ -23,6 +19,8 @@ import { LocalReactionType, LocalUserType, } from '../../types'; +import {normalize} from '../../utils'; +import {ChatInputSubmit} from '../messages'; const ChatInput: React.FC< MessageInputProps< @@ -37,7 +35,35 @@ const ChatInput: React.FC< > = () => { const state: RootState = useStore().getState(); const avatar = state.user.avatar; - const {sendMessage, setText, text} = useMessageInputContext(); + const {sendMessage, text, setText, uploadNewImage} = useMessageInputContext(); + const { + setSelectedImages, + selectedImages, + openPicker, + } = useAttachmentPickerContext(); + + const selectImage = () => { + ImagePicker.openPicker({ + cropping: true, + freeStyleCropEnabled: true, + mediaType: 'photo', + multiple: true, + // includeBase64: true, + }) + .then((pictures) => { + pictures.map((pic) => + uploadNewImage({ + width: pic.width, + height: pic.height, + source: 'picker', + uri: 'ph://' + pic.localIdentifier, + }), + ); + }) + .catch((error) => { + console.log(error); + }); + }; return ( <View style={styles.container}> @@ -50,18 +76,23 @@ const ChatInput: React.FC< : require('../../assets/images/avatar-placeholder.png') } /> - <TextInput - style={styles.text} - placeholder={'Message...'} - placeholderTextColor="grey" - multiline={true} - value={text} - onChangeText={setText} - /> - <View style={styles.submitButton}> - <TouchableOpacity style={styles.submitButton} onPress={sendMessage}> - <UpArrowIcon width={35} height={35} color={'white'} /> + <AutoCompleteInput /> + <View style={styles.actionButtons}> + {/* TODO: Not working, toggled off for now */} + {/* <TouchableOpacity onPress={openPicker}> */} + {/* <TouchableOpacity onPress={selectImage}> + <Image + style={{width: normalize(23), height: normalize(23)}} + source={require('../../assets/images/camera.png')} + /> + </TouchableOpacity> */} + <TouchableOpacity onPress={() => setText('/')}> + <Image + style={{width: normalize(23), height: normalize(23)}} + source={require('../../assets/images/gif.png')} + /> </TouchableOpacity> + <ChatInputSubmit onPress={sendMessage} outlined={text.length === 0} /> </View> </View> </View> @@ -72,6 +103,7 @@ const styles = StyleSheet.create({ container: { alignItems: 'center', width: '100%', + top: -10, }, textContainer: { width: '95%', @@ -88,28 +120,27 @@ const styles = StyleSheet.create({ marginHorizontal: '1%', }, avatar: { - height: 35, - width: 35, + height: normalize(30), + width: normalize(30), borderRadius: 30, marginRight: 10, marginLeft: '3%', - marginVertical: '2%', - alignSelf: 'flex-end', - }, - submitButton: { - height: 35, - width: 35, - backgroundColor: TAGG_LIGHT_BLUE, - borderRadius: 999, - justifyContent: 'center', - alignItems: 'center', - marginRight: '3%', - marginVertical: '2%', + marginVertical: 5, alignSelf: 'flex-end', }, whiteBackround: { backgroundColor: '#fff', }, + actionButtons: { + height: normalize(30) + 10, + flexDirection: 'row', + justifyContent: 'space-evenly', + alignItems: 'center', + marginRight: 10, + width: 100, + alignSelf: 'flex-end', + // borderWidth: 1, + }, }); export default ChatInput; diff --git a/src/components/messages/ChatInputSubmit.tsx b/src/components/messages/ChatInputSubmit.tsx new file mode 100644 index 00000000..9e29ad4d --- /dev/null +++ b/src/components/messages/ChatInputSubmit.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import {StyleSheet, TouchableOpacity} from 'react-native'; +import UpArrowIcon from '../../assets/icons/up_arrow.svg'; +import {TAGG_LIGHT_BLUE} from '../../constants'; +import {normalize} from '../../utils'; + +interface ChatInputSubmitProps { + outlined: boolean; + onPress: () => void; +} + +const SIZE = normalize(25); + +const ChatInputSubmit: React.FC<ChatInputSubmitProps> = (props) => { + const {outlined, onPress} = props; + + return outlined ? ( + <TouchableOpacity + style={[styles.submitButton, styles.outline]} + onPress={onPress}> + <UpArrowIcon width={SIZE} height={SIZE} color={TAGG_LIGHT_BLUE} /> + </TouchableOpacity> + ) : ( + <TouchableOpacity + style={[styles.submitButton, styles.background]} + onPress={onPress}> + <UpArrowIcon width={SIZE} height={SIZE} color={'white'} /> + </TouchableOpacity> + ); +}; + +const styles = StyleSheet.create({ + submitButton: { + height: SIZE, + aspectRatio: 1, + borderRadius: 999, + justifyContent: 'center', + alignItems: 'center', + }, + background: { + backgroundColor: TAGG_LIGHT_BLUE, + }, + outline: { + borderWidth: 1, + borderColor: TAGG_LIGHT_BLUE, + }, +}); + +export default ChatInputSubmit; diff --git a/src/components/messages/index.ts b/src/components/messages/index.ts index 83ecd2ad..c809d3d1 100644 --- a/src/components/messages/index.ts +++ b/src/components/messages/index.ts @@ -1,3 +1,5 @@ export {default as MessagesHeader} from './MessagesHeader'; export {default as ChannelPreview} from './ChannelPreview'; export {default as ChatInput} from './ChatInput'; +export {default as ChatHeader} from './ChatHeader'; +export {default as ChatInputSubmit} from './ChatInputSubmit'; diff --git a/src/components/profile/Content.tsx b/src/components/profile/Content.tsx index 0052b61d..c70d6df5 100644 --- a/src/components/profile/Content.tsx +++ b/src/components/profile/Content.tsx @@ -1,14 +1,9 @@ -import React, { - useCallback, - useContext, - useEffect, - useRef, - useState, -} from 'react'; +import {useScrollToTop} from '@react-navigation/native'; +import React, {useCallback, useEffect, useRef, useState} from 'react'; import {LayoutChangeEvent, RefreshControl, StyleSheet} from 'react-native'; import Animated, { - useSharedValue, useAnimatedScrollHandler, + useSharedValue, } from 'react-native-reanimated'; import {useDispatch, useSelector, useStore} from 'react-redux'; import { @@ -36,8 +31,6 @@ import ProfileBody from './ProfileBody'; 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; @@ -59,8 +52,6 @@ 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. */ @@ -77,7 +68,6 @@ const Content: React.FC<ContentProps> = ({userXId, screenType}) => { const [isBlocked, setIsBlocked] = useState<boolean>(false); const [profileBodyHeight, setProfileBodyHeight] = useState(0); const [socialsBarHeight, setSocialsBarHeight] = useState(0); - const [shouldBounce, setShouldBounce] = useState<boolean>(true); const [refreshing, setRefreshing] = useState<boolean>(false); const onRefresh = useCallback(() => { diff --git a/src/screens/chat/ChatScreen.tsx b/src/screens/chat/ChatScreen.tsx index 682906ee..161bd07d 100644 --- a/src/screens/chat/ChatScreen.tsx +++ b/src/screens/chat/ChatScreen.tsx @@ -1,24 +1,20 @@ import {useBottomTabBarHeight} from '@react-navigation/bottom-tabs'; import {StackNavigationProp} from '@react-navigation/stack'; -import React, {useContext} from 'react'; -import {Image, StyleSheet, Text, View} from 'react-native'; -import {SafeAreaView} from 'react-native-safe-area-context'; -import {useStore} from 'react-redux'; +import React, {useContext, useEffect} from 'react'; +import {StyleSheet} from 'react-native'; +import {SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-context'; import { Channel, Chat, MessageInput, MessageList, - OverlayProvider, - useMessageContext, + useAttachmentPickerContext, } from 'stream-chat-react-native'; import {ChatContext} from '../../App'; -import ChatHeader from '../../components/messages/ChatHeader'; -import {TAGG_LIGHT_BLUE} from '../../constants'; +import {ChatHeader, ChatInput, TabsGradient} from '../../components'; import {MainStackParams} from '../../routes'; -import {RootState} from '../../store/rootReducer'; import {ScreenType} from '../../types'; -import {isIPhoneX, SCREEN_WIDTH} from '../../utils'; +import {HeaderHeight, isIPhoneX} from '../../utils'; type ChatScreenNavigationProp = StackNavigationProp<MainStackParams, 'Chat'>; interface ChatScreenProps { @@ -29,9 +25,9 @@ interface ChatScreenProps { */ const ChatScreen: React.FC<ChatScreenProps> = () => { const {channel, chatClient} = useContext(ChatContext); - const state: RootState = useStore().getState(); - const loggedInUserId = state.user.user.userId; const tabbarHeight = useBottomTabBarHeight(); + const {setTopInset} = useAttachmentPickerContext(); + const insets = useSafeAreaInsets(); const chatTheme = { messageList: { @@ -39,52 +35,17 @@ const ChatScreen: React.FC<ChatScreenProps> = () => { backgroundColor: 'white', }, }, + messageInput: { + container: { + backgroundColor: '#f8f8f8', + height: 70, + }, + }, }; - const isOwnMessage = (message) => { - if (message.user.id === loggedInUserId) { - return true; - } else { - return false; - } - }; - - const OwnMessageBubble = ({message}) => ( - <View style={[styles.mainBubbleContainer, styles.mainOwnBubbleContainer]}> - <View style={styles.ownBubbleContainer}> - <View style={styles.ownBubble}> - <Text style={styles.messageText}>{message.text}</Text> - </View> - </View> - {/* TODO: Timestamp */} - </View> - ); - - const UserXMessageBubble = ({message}) => ( - <View style={[styles.mainBubbleContainer, styles.mainUserXBubbleContainer]}> - <Image - style={styles.avatar} - source={ - message.user.thumbnail_url - ? {uri: message.user.thumbnail_url} - : require('../../assets/images/avatar-placeholder.png') - } - /> - <View style={styles.userXBubble}> - <Text style={styles.messageText}>{message.text}</Text> - </View> - {/* TODO: Timestamp */} - </View> - ); - - const CustomMessageUIComponent = () => { - const {message} = useMessageContext(); - if (isOwnMessage(message)) { - return <OwnMessageBubble message={message} />; - } else { - return <UserXMessageBubble message={message} />; - } - }; + useEffect(() => { + setTopInset(insets.top + HeaderHeight); + }); return ( <SafeAreaView @@ -95,23 +56,20 @@ const ChatScreen: React.FC<ChatScreenProps> = () => { ]}> <ChatHeader screenType={ScreenType.Chat} /> <Chat client={chatClient} style={chatTheme}> - <OverlayProvider topInset={0} bottomInset={0}> - <Channel - channel={channel} - keyboardVerticalOffset={0} - OverlayReactionList={() => null} - // MessageSimple={CustomMessageUIComponent} - messageActions={({copyMessage, deleteMessage}) => [ - copyMessage, - deleteMessage, - ]} - // AttachButton={() => null} - > - <MessageList onThreadSelect={() => {}} /> - <MessageInput /> - </Channel> - </OverlayProvider> + <Channel + channel={channel} + keyboardVerticalOffset={0} + OverlayReactionList={() => null} + messageActions={({copyMessage, deleteMessage}) => [ + copyMessage, + deleteMessage, + ]}> + <MessageList onThreadSelect={() => {}} /> + {/* <MessageInput /> */} + <MessageInput Input={ChatInput} /> + </Channel> </Chat> + <TabsGradient /> </SafeAreaView> ); }; @@ -121,65 +79,6 @@ const styles = StyleSheet.create({ backgroundColor: 'white', flex: 1, }, - - submitButton: { - height: 35, - width: 35, - backgroundColor: TAGG_LIGHT_BLUE, - borderRadius: 999, - justifyContent: 'center', - alignItems: 'center', - bottom: -5, - alignSelf: 'flex-end', - }, - messageText: { - width: 196, - paddingHorizontal: 23, - paddingTop: 7.35, - paddingBottom: 12, - }, - mainBubbleContainer: { - marginVertical: 1, - width: SCREEN_WIDTH, - flexDirection: 'row', - }, - mainOwnBubbleContainer: { - justifyContent: 'flex-end', // Different - marginTop: 22, - }, - mainUserXBubbleContainer: { - justifyContent: 'flex-start', - }, - avatar: { - width: 40, - height: 40, - borderRadius: 20, - right: -19, - zIndex: 1, - position: 'absolute', - top: 0, - left: 0, - }, - ownBubbleContainer: {width: 241, marginBottom: 9}, - ownBubble: { - maxWidth: 270, - backgroundColor: '#DEE6F4', - borderColor: '#DEE6F4', - borderWidth: 1, - borderRadius: 10, - alignSelf: 'center', - }, - userXBubble: { - maxWidth: 235, - backgroundColor: '#E4F0F2', - borderColor: '#E4F0F2', - borderWidth: 1, - borderRadius: 10, - zIndex: 0, - alignSelf: 'flex-end', - marginLeft: 25, - marginTop: 14, - }, }); export default ChatScreen; |