Created
November 3, 2020 10:05
-
-
Save vishalnarkhede/55c4955c00c1110511f51fd7b514e286 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src/screens/MessageSearchScreen.js | |
import React, {useEffect, useRef, useState} from 'react'; | |
import {View, StyleSheet} from 'react-native'; | |
import { | |
FlatList, | |
TextInput, | |
TouchableOpacity, | |
ActivityIndicator, | |
SafeAreaView, | |
} from 'react-native'; | |
import { | |
AsyncStore, | |
ChatClientService, | |
getChannelDisplayName, | |
useStreamChatTheme, | |
} from '../utils'; | |
import { | |
Message as DefaultMessage, | |
ThemeProvider, | |
} from 'stream-chat-react-native'; | |
import {useNavigation, useTheme} from '@react-navigation/native'; | |
import {MessageSlack} from '../components/MessageSlack'; | |
import {SCText} from '../components/SCText'; | |
import {ListItemSeparator} from '../components/ListItemSeparator'; | |
export const MessageSearchScreen = () => { | |
const {colors, dark} = useTheme(); | |
const navigation = useNavigation(); | |
const chatStyle = useStreamChatTheme(); | |
const inputRef = useRef(null); | |
const [results, setResults] = useState(null); | |
const [recentSearches, setRecentSearches] = useState([]); | |
const [loadingResults, setLoadingResults] = useState(false); | |
const [searchText, setSearchText] = useState(''); | |
const addToRecentSearches = async q => { | |
const _recentSearches = [...recentSearches]; | |
_recentSearches.unshift(q); | |
// Store only max 10 searches | |
const slicesRecentSearches = _recentSearches.slice(0, 7); | |
setRecentSearches(slicesRecentSearches); | |
await AsyncStore.setItem( | |
'@slack-clone-recent-searches', | |
slicesRecentSearches, | |
); | |
}; | |
const removeFromRecentSearches = async index => { | |
const _recentSearches = [...recentSearches]; | |
_recentSearches.splice(index, 1); | |
setRecentSearches(_recentSearches); | |
await AsyncStore.setItem('@slack-clone-recent-searches', _recentSearches); | |
}; | |
const search = async q => { | |
if (!q) { | |
setLoadingResults(false); | |
return; | |
} | |
const chatClient = ChatClientService.getClient(); | |
try { | |
const res = await chatClient.search( | |
{ | |
members: { | |
$in: [chatClient.user.id], | |
}, | |
}, | |
q, | |
{limit: 10, offset: 0}, | |
); | |
setResults(res.results.map(r => r.message)); | |
} catch (error) { | |
setResults([]); | |
} | |
setLoadingResults(false); | |
addToRecentSearches(q); | |
}; | |
const startNewSearch = () => { | |
setSearchText(''); | |
setResults(null); | |
setLoadingResults(false); | |
inputRef.current.focus(); | |
}; | |
useEffect(() => { | |
const loadRecentSearches = async () => { | |
const recentSearches = await AsyncStore.getItem( | |
'@slack-clone-recent-searches', | |
[], | |
); | |
setRecentSearches(recentSearches); | |
}; | |
loadRecentSearches(); | |
}, []); | |
return ( | |
<SafeAreaView | |
style={[ | |
styles.safeAreaView, | |
{ | |
backgroundColor: colors.background, | |
}, | |
]}> | |
<View style={styles.container}> | |
<View | |
style={[ | |
styles.headerContainer, | |
{ | |
backgroundColor: colors.backgroundSecondary, | |
}, | |
]}> | |
<TextInput | |
ref={ref => { | |
inputRef.current = ref; | |
}} | |
returnKeyType="search" | |
autoFocus | |
value={searchText} | |
onChangeText={text => { | |
setSearchText(text); | |
setResults(null); | |
}} | |
onSubmitEditing={({nativeEvent: {text, eventCount, target}}) => { | |
setLoadingResults(true); | |
search(text); | |
}} | |
placeholder="Search for message" | |
placeholderTextColor={colors.text} | |
style={[ | |
styles.inputBox, | |
{ | |
backgroundColor: dark ? '#363639' : '#dcdcdc', | |
borderColor: dark ? '#212527' : '#D3D3D3', | |
color: colors.text, | |
}, | |
]} | |
/> | |
<TouchableOpacity | |
style={styles.cancelButton} | |
onPress={() => { | |
navigation.goBack(); | |
}}> | |
<SCText>Cancel</SCText> | |
</TouchableOpacity> | |
</View> | |
{results && results.length > 0 && ( | |
<View | |
style={[ | |
styles.resultCountContainer, | |
{ | |
backgroundColor: colors.background, | |
borderColor: colors.border, | |
}, | |
]}> | |
<SCText>{results.length} Results</SCText> | |
</View> | |
)} | |
<View | |
style={[ | |
styles.recentSearchesContainer, | |
{ | |
backgroundColor: colors.background, | |
}, | |
]}> | |
{!results && !loadingResults && ( | |
<> | |
<SCText | |
style={[ | |
styles.recentSearchesTitle, | |
{ | |
backgroundColor: colors.backgroundSecondary, | |
}, | |
]}> | |
Recent searches | |
</SCText> | |
<FlatList | |
keyboardShouldPersistTaps="always" | |
ItemSeparatorComponent={ListItemSeparator} | |
data={recentSearches} | |
renderItem={({item, index}) => { | |
return ( | |
<TouchableOpacity | |
onPress={() => { | |
setSearchText(item); | |
}} | |
style={styles.recentSearchItemContainer}> | |
<SCText style={styles.recentSearchText}>{item}</SCText> | |
<SCText | |
onPress={() => { | |
removeFromRecentSearches(index); | |
}}> | |
X | |
</SCText> | |
</TouchableOpacity> | |
); | |
}} | |
/> | |
</> | |
)} | |
{loadingResults && ( | |
<View style={styles.loadingIndicatorContainer}> | |
<ActivityIndicator size="small" color="black" /> | |
</View> | |
)} | |
{results && ( | |
<View style={styles.resultsContainer}> | |
<FlatList | |
keyboardShouldPersistTaps="always" | |
contentContainerStyle={{flexGrow: 1}} | |
ListEmptyComponent={() => { | |
return ( | |
<View style={styles.listEmptyContainer}> | |
<SCText>No results for "{searchText}"</SCText> | |
<TouchableOpacity | |
onPress={startNewSearch} | |
style={styles.resetButton}> | |
<SCText>Start a new search</SCText> | |
</TouchableOpacity> | |
</View> | |
); | |
}} | |
data={results} | |
renderItem={({item}) => { | |
return ( | |
<TouchableOpacity | |
onPress={() => { | |
navigation.navigate('TargettedMessageChannelScreen', { | |
message: item, | |
}); | |
}} | |
style={[ | |
styles.resultItemContainer, | |
{ | |
backgroundColor: colors.background, | |
}, | |
]}> | |
<SCText style={styles.resultChannelTitle}> | |
{getChannelDisplayName(item.channel, true)} | |
</SCText> | |
<ThemeProvider style={chatStyle}> | |
<DefaultMessage | |
Message={props => ( | |
<MessageSlack | |
{...props} | |
onPress={() => { | |
navigation.navigate( | |
'TargettedMessageChannelScreen', | |
{ | |
message: item, | |
}, | |
); | |
}} | |
/> | |
)} | |
message={item} | |
groupStyles={['single']} | |
/> | |
</ThemeProvider> | |
</TouchableOpacity> | |
); | |
}} | |
/> | |
</View> | |
)} | |
</View> | |
</View> | |
</SafeAreaView> | |
); | |
}; | |
const styles = StyleSheet.create({ | |
safeAreaView: { | |
flex: 1, | |
height: '100%', | |
}, | |
container: { | |
flexDirection: 'column', | |
height: '100%', | |
}, | |
headerContainer: { | |
flexDirection: 'row', | |
width: '100%', | |
padding: 10, | |
}, | |
inputBox: { | |
flex: 1, | |
margin: 3, | |
padding: 10, | |
borderWidth: 0.5, | |
borderRadius: 10, | |
}, | |
cancelButton: {justifyContent: 'center', padding: 5}, | |
resultCountContainer: { | |
padding: 15, | |
borderBottomWidth: 0.5, | |
}, | |
recentSearchesContainer: { | |
marginTop: 10, | |
marginBottom: 10, | |
flexGrow: 1, | |
flexShrink: 1, | |
}, | |
recentSearchesTitle: { | |
padding: 5, | |
fontSize: 13, | |
}, | |
recentSearchItemContainer: { | |
padding: 10, | |
justifyContent: 'space-between', | |
flexDirection: 'row', | |
}, | |
recentSearchText: {fontSize: 14}, | |
loadingIndicatorContainer: { | |
flexGrow: 1, | |
flexShrink: 1, | |
alignItems: 'center', | |
justifyContent: 'center', | |
}, | |
resultsContainer: {flexGrow: 1, flexShrink: 1}, | |
listEmptyContainer: { | |
flex: 1, | |
alignItems: 'center', | |
justifyContent: 'center', | |
}, | |
resetButton: { | |
padding: 15, | |
paddingTop: 10, | |
paddingBottom: 10, | |
marginTop: 10, | |
borderColor: '#696969', | |
borderWidth: 0.5, | |
borderRadius: 5, | |
}, | |
resultItemContainer: { | |
padding: 10, | |
}, | |
resultChannelTitle: { | |
paddingTop: 10, | |
paddingBottom: 10, | |
fontWeight: '700', | |
color: '#8b8b8b', | |
}, | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src/screens/TargettedMessageChannelScreen.js | |
import {useNavigation, useRoute, useTheme} from '@react-navigation/native'; | |
import React, {useEffect, useState} from 'react'; | |
import {View, SafeAreaView, StyleSheet, Text} from 'react-native'; | |
import {TouchableOpacity} from 'react-native-gesture-handler'; | |
import {Chat, Channel, MessageList} from 'stream-chat-react-native'; | |
import {ChannelHeader} from '../components/ChannelHeader'; | |
import {CustomKeyboardCompatibleView} from '../components/CustomKeyboardCompatibleView'; | |
import {DateSeparator} from '../components/DateSeparator'; | |
import {MessageSlack} from '../components/MessageSlack'; | |
import {ChatClientService, useStreamChatTheme} from '../utils'; | |
export const TargettedMessageChannelScreen = () => { | |
const chatTheme = useStreamChatTheme(); | |
const navigation = useNavigation(); | |
const { | |
params: {message = null}, | |
} = useRoute(); | |
const {colors} = useTheme(); | |
const chatClient = ChatClientService.getClient(); | |
const [channel, setChannel] = useState(null); | |
useEffect(() => { | |
const initChannel = async () => { | |
if (!message) { | |
navigation.goBack(); | |
} else { | |
const _channel = chatClient.channel('messaging', message.channel.id); | |
const res = await _channel.query({ | |
messages: {limit: 10, id_lte: message.id}, | |
}); | |
// We are tricking Channel component from stream-chat-react-native into believing | |
// that provided channel is initialized, so that it doesn't call .watch() on channel. | |
_channel.initialized = true; | |
setChannel(_channel); | |
} | |
}; | |
initChannel(); | |
}, [message]); | |
if (!channel) { | |
return null; | |
} | |
return ( | |
<SafeAreaView | |
style={{ | |
backgroundColor: colors.background, | |
}}> | |
<View style={styles.channelScreenContainer}> | |
<ChannelHeader channel={channel} goBack={navigation.goBack} /> | |
<View style={styles.chatContainer}> | |
<Chat client={chatClient} style={chatTheme}> | |
<Channel | |
channel={channel} | |
KeyboardCompatibleView={CustomKeyboardCompatibleView}> | |
<MessageList | |
Message={MessageSlack} | |
DateSeparator={DateSeparator} | |
additionalFlatListProps={{ | |
onEndReached: () => null, | |
}} | |
/> | |
</Channel> | |
</Chat> | |
</View> | |
<TouchableOpacity | |
style={[ | |
styles.recentMessageLink, | |
{ | |
backgroundColor: colors.primary, | |
}, | |
]} | |
onPress={() => { | |
channel.initialized = false; | |
navigation.navigate('ChannelScreen', { | |
channelId: channel.id, | |
}); | |
}}> | |
<Text style={styles.recentMessageLinkText}> | |
Jump to recent message | |
</Text> | |
</TouchableOpacity> | |
</View> | |
</SafeAreaView> | |
); | |
} | |
const styles = StyleSheet.create({ | |
channelScreenSaveAreaView: { | |
backgroundColor: '#F7F7F7', | |
}, | |
channelScreenContainer: {flexDirection: 'column', height: '100%'}, | |
container: { | |
flex: 1, | |
backgroundColor: 'white', | |
}, | |
drawerNavigator: { | |
backgroundColor: '#3F0E40', | |
width: 350, | |
}, | |
chatContainer: { | |
flexGrow: 1, | |
flexShrink: 1, | |
}, | |
recentMessageLink: { | |
height: 60, | |
alignSelf: 'center', | |
width: '100%', | |
paddingTop: 20, | |
}, | |
recentMessageLinkText: { | |
alignSelf: 'center', | |
color: '#1E90FF', | |
fontSize: 15, | |
}, | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment