Skip to content

Instantly share code, notes, and snippets.

@vishalnarkhede
Last active November 3, 2020 21:46
Show Gist options
  • Save vishalnarkhede/f337314921224c57bcf0950482611bab to your computer and use it in GitHub Desktop.
Save vishalnarkhede/f337314921224c57bcf0950482611bab to your computer and use it in GitHub Desktop.
// src/components/InpuBoxThread.js
import React, {useRef, useState} from 'react';
import {TouchableOpacity, Animated, View, StyleSheet} from 'react-native';
import {
AutoCompleteInput,
SendButton,
useChannelContext,
} from 'stream-chat-react-native';
import {getChannelDisplayName} from '../utils';
import {useTheme} from '@react-navigation/native';
import {SVGIcon} from './SVGIcon';
import CheckBox from '@react-native-community/checkbox';
import {SCText} from './SCText';
export const InputBoxThread = props => {
const {colors} = useTheme();
const [leftMenuActive, setLeftMenuActive] = useState(true);
const {channel} = useChannelContext();
const transform = useRef(new Animated.Value(0)).current;
const translateMenuLeft = useRef(new Animated.Value(0)).current;
const translateMenuRight = useRef(new Animated.Value(300)).current;
const opacityMenuLeft = useRef(new Animated.Value(1)).current;
const opacityMenuRight = useRef(new Animated.Value(0)).current;
const isDirectMessagingConversation = !channel.data.name;
return (
<View style={[styles.container, {backgroundColor: colors.background}]}>
<AutoCompleteInput {...props} />
<View
style={[styles.actionsContainer, {backgroundColor: colors.background}]}>
<Animated.View // Special animatable View
style={{
transform: [
{
rotate: transform.interpolate({
inputRange: [0, 180],
outputRange: ['0deg', '180deg'],
}),
},
{perspective: 1000},
], // Bind opacity to animated value
}}>
<TouchableOpacity
onPress={() => {
Animated.parallel([
Animated.timing(transform, {
toValue: leftMenuActive ? 180 : 0,
duration: 200,
useNativeDriver: false,
}),
Animated.timing(translateMenuLeft, {
toValue: leftMenuActive ? -300 : 0,
duration: 200,
useNativeDriver: false,
}),
Animated.timing(translateMenuRight, {
toValue: leftMenuActive ? 0 : 300,
duration: 200,
useNativeDriver: false,
}),
Animated.timing(opacityMenuLeft, {
toValue: leftMenuActive ? 0 : 1,
duration: leftMenuActive ? 50 : 200,
useNativeDriver: false,
}),
Animated.timing(opacityMenuRight, {
toValue: leftMenuActive ? 1 : 0,
duration: leftMenuActive ? 50 : 200,
useNativeDriver: false,
}),
]).start();
setLeftMenuActive(!leftMenuActive);
}}
style={[
{
padding: 1.5,
paddingRight: 6,
paddingLeft: 6,
borderRadius: 10,
backgroundColor: colors.linkText,
},
]}>
<SCText style={{fontWeight: '900', color: 'white'}}>{'<'}</SCText>
</TouchableOpacity>
</Animated.View>
<View
style={{
flexGrow: 1,
flexShrink: 1,
flexDirection: 'row',
marginLeft: 20,
}}>
<Animated.View
style={{
flexDirection: 'row',
alignItems: 'center',
transform: [{translateX: translateMenuLeft}],
opacity: opacityMenuLeft,
}}>
<CheckBox
boxType="square"
disabled={false}
style={{width: 15, height: 15}}
onValueChange={newValue =>
props.setSendMessageInChannel(newValue)
}
/>
<SCText style={{marginLeft: 12, fontSize: 14}}>
Also send to{' '}
{isDirectMessagingConversation
? 'group'
: getChannelDisplayName(channel, true)}
</SCText>
</Animated.View>
<Animated.View
style={{
position: 'absolute',
width: '100%',
alignItems: 'center',
alignSelf: 'center',
justifyContent: 'center',
flexDirection: 'row',
transform: [
{translateX: translateMenuRight},
{perspective: 1000},
],
opacity: opacityMenuRight,
}}>
<View style={styles.row}>
<TouchableOpacity
onPress={() => {
props.appendText('@');
}}>
<SCText style={styles.textActionLabel}>@</SCText>
</TouchableOpacity>
{/* Text editor is not functional yet. We will cover it in some future tutorials */}
<TouchableOpacity style={styles.textEditorContainer}>
<SCText style={styles.textActionLabel}>Aa</SCText>
</TouchableOpacity>
</View>
<View
style={[
styles.row,
{
justifyContent: 'flex-end',
},
]}>
<TouchableOpacity
onPress={props._pickFile}
style={styles.fileAttachmentIcon}>
<SVGIcon type="file-attachment" height="18" width="18" />
</TouchableOpacity>
<TouchableOpacity
onPress={props._pickImage}
style={styles.imageAttachmentIcon}>
<SVGIcon type="image-attachment" height="18" width="18" />
</TouchableOpacity>
</View>
</Animated.View>
</View>
<SendButton
{...props}
sendMessage={() => {
props.sendMessage(props.channel);
}}
/>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flexDirection: 'column',
width: '100%',
height: 60,
},
actionsContainer: {
flexDirection: 'row',
width: '100%',
alignItems: 'center',
},
row: {
flex: 1,
flexDirection: 'row',
width: '100%',
},
textActionLabel: {
fontSize: 18,
},
textEditorContainer: {
marginLeft: 10,
},
fileAttachmentIcon: {
marginRight: 10,
marginLeft: 10,
alignSelf: 'center',
},
imageAttachmentIcon: {
marginRight: 10,
marginLeft: 10,
alignSelf: 'flex-end',
},
});
// src/screens/ThreadScreen.js
import React, {useEffect, useState} from 'react';
import {View, SafeAreaView, Platform, StyleSheet} from 'react-native';
import {
Chat,
Channel,
KeyboardCompatibleView,
Thread,
Message as DefaultMessage,
} from 'stream-chat-react-native';
import {useNavigation, useTheme} from '@react-navigation/native';
import {MessageSlack} from '../components/MessageSlack';
import {
getChannelDisplayName,
useStreamChatTheme,
ChatClientService,
truncate,
} from '../utils';
import {ModalScreenHeader} from '../components/ModalScreenHeader';
import {InputBoxThread} from '../components/InputBoxThread';
import {SVGIcon} from '../components/SVGIcon';
import {SCText} from '../components/SCText';
const CustomKeyboardCompatibleView = ({children}) => (
<KeyboardCompatibleView
keyboardVerticalOffset={Platform.OS === 'ios' ? 120 : -200}
behavior={Platform.OS === 'ios' ? 'padding' : 'position'}>
{children}
</KeyboardCompatibleView>
);
export function ThreadScreen({
route: {
params: {channelId = null, threadId = null},
},
}) {
const {colors} = useTheme();
const chatStyles = useStreamChatTheme();
const chatClient = ChatClientService.getClient();
const navigation = useNavigation();
const [channel, setChannel] = useState(null);
const [thread, setThread] = useState();
const [sendMessageInChannel, setSendMessageInChannel] = useState(false);
const [isReady, setIsReady] = useState(false);
const goBack = () => {
navigation.goBack();
};
useEffect(() => {
const getThread = async () => {
const res = await chatClient.getMessage(threadId);
setThread(res.message);
};
getThread();
}, [chatClient, threadId]);
useEffect(() => {
if (!channelId) {
navigation.goBack();
} else {
const _channel = chatClient.channel('messaging', channelId);
setChannel(_channel);
setIsReady(true);
}
}, [channelId, threadId]);
if (!isReady || !thread) {
return null;
}
return (
<SafeAreaView
style={{
backgroundColor: colors.background,
}}>
<View style={styles.channelScreenContainer}>
<ModalScreenHeader
title={'Thread'}
goBack={goBack}
subTitle={truncate(getChannelDisplayName(channel, true), 35)}
/>
<View
style={[
styles.chatContainer,
{
backgroundColor: colors.background,
},
]}>
<Chat client={chatClient} style={chatStyles}>
<Channel
channel={channel}
thread={thread}
doSendMessageRequest={async (cid, message) => {
const newMessage = {
...message,
show_in_channel: sendMessageInChannel,
parentMessageText: sendMessageInChannel
? thread.text
: undefined,
};
return channel.sendMessage(newMessage);
}}
KeyboardCompatibleView={CustomKeyboardCompatibleView}>
<Thread
additionalMessageInputProps={{
Input: props => (
<InputBoxThread
{...props}
setSendMessageInChannel={setSendMessageInChannel}
/>
),
additionalTextInputProps: {
placeholderTextColor: '#979A9A',
placeholder:
channel && channel.data.name
? 'Message #' +
channel.data.name.toLowerCase().replace(' ', '_')
: 'Message',
},
}}
additionalMessageListProps={{
Message: MessageSlack,
DateSeparator: () => null,
HeaderComponent: () => {
return (
<>
<DefaultMessage
groupStyles={['single']}
message={thread}
Message={MessageSlack}
threadList
/>
<View
style={[
styles.threadHeaderSeparator,
{
backgroundColor: colors.background,
borderTopColor: colors.border,
borderBottomColor: colors.border,
},
]}>
{thread.reply_count > 0 ? (
<SCText>{thread.reply_count} replies</SCText>
) : (
<View
style={styles.emptyThreadHeaderSeparatorContent}>
<SVGIcon type="threads" height="15" width="15" />
<SCText style={{marginLeft: 10}}>
reply in thread
</SCText>
</View>
)}
</View>
</>
);
},
}}
/>
</Channel>
</Chat>
</View>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
channelScreenContainer: {flexDirection: 'column', height: '100%'},
container: {
flex: 1,
backgroundColor: 'white',
},
drawerNavigator: {
backgroundColor: '#3F0E40',
width: 350,
},
chatContainer: {
flexGrow: 1,
flexShrink: 1,
},
threadHeaderSeparator: {
padding: 10,
borderTopWidth: 1,
borderBottomWidth: 1,
marginBottom: 20,
},
emptyThreadHeaderSeparatorContent: {
flexDirection: 'row',
alignItems: 'center',
},
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment