Custom Send button and clear textInput react-native-gifted-chat - react-native

I'm using react-native-gifted-chat and have a custom Send button, but how can I properly call onSend using my own Send button and after that how can I clear the inpuxText element?
Thanks.

You can define the renderSend function:
renderSend = (sendProps) => {
<TouchableOpacity>
<Image source={require('path/to/your/button/icon')} />
</TouchableOpacity>
);
}
<GiftedChat renderSend={this.renderSend} />
More info here: https://github.com/FaridSafi/react-native-gifted-chat/issues/480
As for clearing the text input, perhaps you could use redux and clear the textInput by returning a blank textInput?
For example:
case MESSAGE_SENT:
return { ...state, error: action.payload, loading: false, textInput: '' };

So it took me a while but I finally got it
<GiftedChat
messages={messages}
textInputStyle={styles.textInput}
onSend={messages => onSend(messages)}
multiline
user={{
_id: 1,
}}
renderSend={(props)=>{
const {text,messageIdGenerator,user, onSend} = props
return(
<TouchableOpacity onPress= {
()=>{
if (text && onSend) {
onSend({ text: text.trim(), user:user,_id:messageIdGenerator()}, true);
}
}
} style={styles.sendButton}>
<Send/>
</TouchableOpacity>
)}}
/>
as for using redux to clear it is a bit redundant, and you will have to work with a large object in redux which is not too go for performance
Just go to the main implementation of send.js in react native gifted chat
https://www.github.com/FaridSafi/react-native-gifted-chat/tree/master/src%2FSend.tsx

Better solution
import { GiftedChat, Send } from 'react-native-gifted-chat'
<Send {...props} >
<View style={{justifyContent: 'center', height: '100%', marginRight: 10}}>
<Icon
name='send'
type='ionicon'
size={24}
color={Colors.primaryColor}
/>
</View>
</Send>

Related

React Navigation is not a function?

I'm using React Navigation. I'm directing you to the conversation after the record insert page. I provide automatic renewal when I redirect. I'm sending a parameter. But that doesn't work. I don't know where I'm making a mistake.
I get this error.
Uncaught typeerror. TypeError: s.handleRefresh is not a function. (In 's.handleRefresh()', 's.handleRefresh' is undefined
LeadDetail.js Page
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
return {
title: 'Müşteri Adayı Detay',
headerRight: (
<TouchableHighlight activeOpacity={0.4} underlayColor='transparent' onPress={() => navigation.navigate('CreateMeeting', { parentId: params.id, parentType: 'Lead' })} style={{marginRight: 12 }}>
<Icon
name="calendar-check-o"
size={25}
color="white"
/>
</TouchableHighlight>
)
};
};
CreateMeeting.js Page
dataSuccess(response) {
this.setState({ buttonLoading: false });
Alert.alert(
'Tebrikler!',
'Meeting başarılı bir şekilde kayıt edildi',
[
{ text: 'Tamam', onPress: () => this.props.navigation.navigate('Meeting', { refreshMeeting: true }) }
]
);
}
Meeting.js Page
static navigationOptions = ({ navigation }) => {
const { params } = navigation.state;
let refresh = navigation.getParam('refreshMeeting', false);
if(refresh) {
params.handleRefresh();
}
return {
title: 'Görüşmeler',
headerRight: (
<View style={{ flexDirection: 'row' }}>
<TouchableHighlight activeOpacity={0.4} underlayColor='transparent' onPress={navigation.getParam('setOverlay')}>
<Icon
name="filter"
size={25}
color="white"
/>
</TouchableHighlight>
<TouchableHighlight activeOpacity={0.4} underlayColor='transparent' onPress={navigation.getParam('setModalVisible')} style={{ marginRight: 12, marginLeft: 20 }}>
<Icon
name="plus"
size={25}
color="white"
/>
</TouchableHighlight>
</View>
)
};
};
Are you setting the param handleRefresh? Instead of params.handleRefresh(); shouldn't it be params.refresh();?
Also, s.handleRefresh where is this in your code? I think there is a typo or something like that and I'm considering that s.handleRefresh is actually params.handleRefresh
Please edit your question if none of this is correct.
I don't know where you are setting the param handleRefresh, but if you are setting it (please check that and add the code where you do that), maybe here is the reason why this happens.
If you look at the docs they have a not
React Navigation doesn't guarantee that your screen component will be mounted before the header. Because the increaseCount param is set in componentDidMount, we may not have it available to us in navigationOptions. This usually will not be a problem because onPress for Button and Touchable components will do nothing if the callback is null. If you have your own custom component here, you should make sure it behaves as expected with null for its press handler prop.
This means that the header can render before the component calls setParams and that doens't load you params in the first time. What you need to do some checking to avoid that time when the header renders first.
// added some checking
if(refresh && params && params.handleRefresh) {
params.handleRefresh();
}

How to set the textinput box above the Keyboard while entering the input field in react native

I am using react-native TextInput component. Here I need to show the InputBox above the keyboard if the user clicks on the textInput field.
I have tried below but i am facing the issues
1. Keyboard avoiding view
a. Here it shows some empty space below the input box
b. Manually I need to scroll up the screen to see the input field which I was given in the text field
c. Input box section is hiding while placing the mouse inside the input box
2. react-native-Keyboard-aware-scroll-view
a.It shows some empty space below the input box
b.ScrollView is reset to the top of the page after I moving to the next input box
Here I set the Keyboard-aware-scroll-view inside the ScrollView component
Kindly clarify
My example code is
<SafeAreaView>
<KeyboardAvoidingView>
<ScrollView>
<Text>Name</Text>
<AutoTags
//required
suggestions={this.state.suggestedName}
handleAddition={this.handleAddition}
handleDelete={this.handleDelete}
multiline={true}
placeholder="TYPE IN"
blurOnSubmit={true}
style= {styles.style}
/>
</ScrollView>
</KeyboardAvoidingView>
</SafeAreaView>
[https://github.com/APSL/react-native-keyboard-aware-scroll-view]
Give your TextInput a position: absolute styling and change its position using the height returned by the keyboardDidShow and keyboardDidHide events.
Here is a modification of the Keyboard example from the React Native documentation for demonstration:
import React, { Component } from 'react';
import { Keyboard, TextInput } from 'react-native';
class Example extends Component {
state = {
keyboardOffset: 0,
};
componentDidMount() {
this.keyboardDidShowListener = Keyboard.addListener(
'keyboardDidShow',
this._keyboardDidShow,
);
this.keyboardDidHideListener = Keyboard.addListener(
'keyboardDidHide',
this._keyboardDidHide,
);
}
componentWillUnmount() {
this.keyboardDidShowListener.remove();
this.keyboardDidHideListener.remove();
}
_keyboardDidShow(event) {
this.setState({
keyboardOffset: event.endCoordinates.height,
})
}
_keyboardDidHide() {
this.setState({
keyboardOffset: 0,
})
}
render() {
return <View style={{flex: 1}}>
<TextInput
style={{
position: 'absolute',
width: '100%',
bottom: this.state.keyboardOffset,
}}
onSubmitEditing={Keyboard.dismiss}
/>
</View>;
}
}
First of all, You don't need any extra code for Android platform. Only keep your inputs inside a ScrollView. Just use KeyboardAvoidingView to encapsulate the ScrollView for iOS platform.
Create function such as below which holds all the inputs
renderInputs = () => {
return (<ScrollView
showsVerticalScrollIndicator={false}
style={{
flex: 1,
}}
contentContainerStyle={{
flexGrow: 1,
}}>
<Text>Enter Email</Text>
<TextInput
style={styles.text}
underlineColorAndroid="transparent"
/>
</ScrollView>)
}
Then render them inside the main view as below
{Platform.OS === 'android' ? (
this.renderInputs()
) : (
<KeyboardAvoidingView behavior="padding">
{this.renderInputs()}
</KeyboardAvoidingView>
)}
I have used this method and I can assure that it works.
If it is not working then there is a chance that you are missing something.
Hooks version:
const [keyboardOffset, setKeyboardOffset] = useState(0);
const onKeyboardShow = event => setKeyboardOffset(event.endCoordinates.height);
const onKeyboardHide = () => setKeyboardOffset(0);
const keyboardDidShowListener = useRef();
const keyboardDidHideListener = useRef();
useEffect(() => {
keyboardDidShowListener.current = Keyboard.addListener('keyboardWillShow', onKeyboardShow);
keyboardDidHideListener.current = Keyboard.addListener('keyboardWillHide', onKeyboardHide);
return () => {
keyboardDidShowListener.current.remove();
keyboardDidHideListener.current.remove();
};
}, []);
You can use a scrollview and put all components inside the scrollview and add automaticallyAdjustKeyboardInsets property to scrollview.it will solve your problem.
automaticallyAdjustKeyboardInsets Controls whether the ScrollView should automatically adjust its contentInset and
scrollViewInsets when the Keyboard changes its size. The default value is false.
<ScrollView automaticallyAdjustKeyboardInsets={true}>
{allChildComponentsHere}
<View style={{ height: 30 }} />//added some extra space to last element
</ScrollView>
Hope it helps.
you can use KeyboardAvoidingView as follows
if (Platform.OS === 'ios') {
return <KeyboardAvoidingView behavior="padding">
{this.renderChatInputSection()}
</KeyboardAvoidingView>
} else {
return this.renderChatInputSection()
}
Where this.renderChatInputSection() will return the view like textinput for typing message. Hope this will help you.
For android you can set android:windowSoftInputMode="adjustResize" for Activity in AndroidManifest file, thus when the keyboard shows, your screen will resize and if you put the TextInput at the bottom of your screen, it will be keep above keyboard
react-native-keyboard-aware-scroll-view caused similar issue in ios. That's when I came across react-native-keyboard-aware-view. Snippets are pretty much same.
<KeyboardAwareView animated={true}>
<View style={{flex: 1}}>
<ScrollView style={{flex: 1}}>
<Text style={{fontSize: 20, color: '#FFFFFF'}}>A</Text>
<Text style={{fontSize: 20, color: '#FFFFFF'}}>B</Text>
<Text style={{fontSize: 20, color: '#FFFFFF'}}>C</Text>
<Text style={{fontSize: 20, color: '#FFFFFF'}}>D</Text>
</ScrollView>
</View>
<TouchableOpacity style={{height: 50, backgroundColor: 'transparent', alignItems: 'center', justifyContent: 'center', alignSelf: 'stretch'}}>
<Text style={{fontSize: 20, color: '#FFFFFF'}}>Submit</Text>
</TouchableOpacity>
</KeyboardAwareView>
Hope it hepls
You will definitely find this useful from
Keyboard aware scroll view Android issue
I really don't know why you have to add
"androidStatusBar": {
"backgroundColor": "#000000"
}
for KeyboardawareScrollview to work
Note:don't forget to restart the project without the last step it might not work
enjoy!
I faced the same problem when I was working on my side project, and I solved it after tweaking KeyboardAvoidingView somewhat.
I published my solution to npm, please give it a try and give me a feedback! Demo on iOS
Example Snippet
import React from 'react';
import { StyleSheet, TextInput } from 'react-native';
import KeyboardStickyView from 'rn-keyboard-sticky-view';
const KeyboardInput = (props) => {
const [value, setValue] = React.useState('');
return (
<KeyboardStickyView style={styles.keyboardView}>
<TextInput
value={value}
onChangeText={setValue}
onSubmitEditing={() => alert(value)}
placeholder="Write something..."
style={styles.input}
/>
</KeyboardStickyView>
);
}
const styles = StyleSheet.create({
keyboardView: { ... },
input: { ... }
});
export default KeyboardInput;
I based my solution of #basbase solution.
My issue with his solution that it makes the TextInput jumps up without any regard for my overall view.
That wasn't what I wanted in my case, so I did as he suggested but with a small modification
Just give the parent View styling like this:
<View
style={{
flex: 1,
bottom: keyboardOffset,
}}>
And it would work! the only issue is that if the keyboard is open and you scrolled down you would see the extra blank padding at the end of the screen.
android:launchMode="singleTask"
android:windowSoftInputMode="stateAlwaysHidden|adjustPan"
write these two lines in your android/app/src/main/AndroidManifest.xml
in activity tag
flexGrow: 1 is the key.
Use it like below:
<ScrollView contentContainerStyle={styles.container}>
<TextInput
label="Note"
value={currentContact.note}
onChangeText={(text) => setAttribute("note", text)}
/>
</ScrollView>
const styles = StyleSheet.create({
container: {
flexGrow: 1,
},
});
Best and Easy Way is to use Scroll View , It will Automatically take content Up and TextInput will not be hide,Can refer Below Code
<ScrollView style={styles.container}>
<View>
<View style={styles.commonView}>
<Image source={firstNameIcon} style={{width: 25, height: 25}}></Image>
<Text style={styles.commonTxt}>First Name</Text>
</View>
<TextInput
onFocus={() => onFocus('firstName')}
placeholder="First Name"
style={styles.txtInput}
onChangeText={(text) => onChangeText(text, 'firstName')}
value={firstNameValue}
/>
</View>
<View>
<View style={styles.commonView}>
<Image source={LastNameIcon} style={{width: 25, height: 25}}></Image>
<Text style={styles.commonTxt}>Last Name</Text>
</View>
<TextInput
onFocus={() => onFocus('lastName')}
placeholder="Last Name"
style={styles.txtInput}
onChangeText={(text) => onChangeText(text, 'lastName')}
value={lastNameValue}
/>
</View>
<View>
<View style={styles.commonView}>
<Image source={callIcon} style={{width: 25, height: 25}}></Image>
<Text style={styles.commonTxt}>Number</Text>
</View>
<TextInput
onFocus={() => onFocus('number')}
placeholder="Number"
style={styles.txtInput}
onChangeText={(text) => onChangeText(text, 'number')}
value={numberValue}
/>
</View>
<View>
<View style={styles.commonView}>
<Image source={emailIcon} style={{width: 25, height: 25}}></Image>
<Text style={styles.commonTxt}>Email</Text>
</View>
<TextInput
onFocus={() => onFocus('email')}
placeholder="Email"
style={styles.txtInput}
onChangeText={(text) => onChangeText(text, 'email')}
value={emailValue}
/>
</View>
<View style={styles.viewSavebtn}>
<TouchableOpacity style={styles.btn}>
<Text style={styles.saveTxt}>Save</Text>
</TouchableOpacity>
</View>
</ScrollView>
go to your Android>app>src>main> AndroidManifest.xml
write these 2 lines :
android:launchMode="singleTop" android:windowSoftInputMode="adjustPan"

React-native-popup-menu on react-navigation header

I'm using redux with react-navigation and would like to show the popup when the user clicks on the button on the react-navigation header-right button.
I wrapped the context menu at the root of my apps, as below
return (
<Provider store={store}>
<MenuContext style={{ flex: 1 }}>
<AppWithNavigationState />
</MenuContext>
</Provider>
)
in one of my screen, I have
static navigationOptions = {
headerTitle: 'News',
headerRight: (
<TouchableOpacity style={{ paddingLeft:15, paddingRight:15 }}>
<Icon name="more-vert" size={30} color="black" />
</TouchableOpacity>
),
}
When the user clicks on the right button, it should be like this
The menu items are dynamic, I will have to pull the data from one of my API and start rendering the menu data.
I've read through online it can be achieved using the context method, but I'm not sure how to implement it in my structure.
Could anyone advise me on this?
And is it possible to render it with my local variable?
The most custom way is to use Modal, when click the right button, called this.refs.modalRef.showModal(), which in your current page:
<View>
<PopupModal ref="modalRef" />
</View>
The PopupModal like this:
export default class PopupModal extends Component {
state = {
show: false,
}
showModal() {
this.setState({show: true});
}
closeModal = () => {
this.setState({show: false});
}
return (
<Modal
transparent
visible={this.state.show}
onRequestClose={this.closeModal}
>
<TouchableWithoutFeedback onPress={this.closeModal}>
<View style={{
width: '100%',
height: '100%',
opacity: 0.5,
backgroundColor: 'gray',
}} />
</TouchableWithoutFeedback>
<View></View> // your designed view, mostly position: 'absolute'
</Modal>
);
}
You can also pass some data to PopupModal by this.refs.modalRef.showModal(data), and in PopupModal:
showModal = (data) => {
this.setState({ data, show: true });
}
https://www.npmjs.com/package/react-native-material-menu
It works to me
headerRight:<View style={{marginRight:10}}>
<Menu
ref={this.setMenuRef}
button={<Text onPress={this.showMenu}><Icon style={{color:screenProps.headerTitleStyle.color,fontSize:25,marginRight:5}} name="md-more"/></Text>}
>
<MenuItem onPress={this.hideMenu}>Rate Us</MenuItem>
<MenuItem onPress={this.hideMenu}>Share App</MenuItem>
<MenuItem onPress={this.hideMenu}>Settings</MenuItem>
</Menu>
</View>,

How to customize look/feel of React Native ListView's RefreshControl

React Native's ListView has a built-in pull-to-refresh control called RefreshControl. It's super easy to use.
I'd like to customize the look and feel of the control to use a different visual design, such as using a material design progress indicator.
How can I customize the look of the RefreshControl in React Native?
You can outsmart it by doing:
setting transparent properties to ListView
Adding component with absolute position
Example:
<View style={{height:Dimensions.get('window').height}}>
{/* custom refresh control */}
<View
style={{position:'absolute',
width:Dimensions.get('window').width, height:60,
alignItems:'center', justifyContent:'center'}}>
<Progress.CircleSnail
color={['red', 'green', 'blue']}
duration={700} />
</View>
{/* list view*/}
<ListView
dataSource={this.state.dataSource}
refreshControl={
<RefreshControl
onLayout={e => console.log(e.nativeEvent)}
// all properties must be transparent
tintColor="transparent"
colors={['transparent']}
style={{backgroundColor: 'transparent'}}
refreshing={this.state.refreshing}
onRefresh={() => {
this.setState({refreshing:true});
setTimeout(() => {
this._addRows()
}, 2000);
}}
/>
}
renderRow={(rowData) => <Text>{rowData}</Text>} />
</View>
This is the result:
You can totally do this. It requires some work though.
You can start by writing something like this.
<View style={styles.scrollview}>
<View style={styles.topBar}><Text style={styles.navText}>PTR Animation</Text></View>
<View style={styles.fillParent}>
<Text>Customer indicator goes here...</Text>
</View>
<View style={styles.fillParent}>
<ListView
style={{flex: 1}}
dataSource={this.state.dataSource}
renderRow={(rowData) => <View style={styles.row}><Text style={styles.text}>{rowData}</Text></View>}
ref='PTRListView'
/>
</View>
</View>
When you'll pull to refresh, you should see the text "Custom indicator goes here..."
Following this pattern, you can place your component instead of just a view and a text.
For the credits, thanks to this article for the idea.
I did it using react-native-pull-to-refresh-custom lib
First create custom loader ListRefreshLoader
import React from 'react';
import {StyleSheet, View} from 'react-native';
import colors from '../../assets/colors';
import {wp} from '../../styles/responsiveScreen';
import Circuler from './Circuler';
const ListRefreshLoader = ({refreshing}) => {
return (
<View>
{refreshing ? (
<View style={styles.container}>
<Circuler color={colors.gray} size={wp(6)} />
</View>
) : null}
</View>
);
};
const styles = StyleSheet.create({
container: {
width: wp(15),
height: wp(15),
alignSelf: 'center',
justifyContent: 'center',
alignItems: 'center',
},
});
export default ListRefreshLoader;
Then use following way
import PullToRefresh from 'react-native-pull-to-refresh-custom';
import ListRefreshLoader from '../../components/Loader/ListRefreshLoader';
<PullToRefresh
HeaderComponent={() => <ListRefreshLoader refreshing={refreshing} />}
headerHeight={60}
refreshTriggerHeight={60}
refreshingHoldHeight={60}
refreshing={refreshing}
onRefresh={onRefresh}
style={styles.list}>
<FlatList
data={friendsList}
viewabilityConfig={{
itemVisiblePercentThreshold: 90,
}}
maxToRenderPerBatch={100}
removeClippedSubviews
style={styles.list}
keyExtractor={(item, index) => index.toString()}
showsVerticalScrollIndicator={false}
persistentScrollbar
renderItem={renderItem}
ItemSeparatorComponent={() => {
return <View style={styles.listSeperator} />;
}}
ListEmptyComponent={totalUserFriends === 0 ? renderEmpty() : null}
ListHeaderComponent={
totalUserFriends !== 0 || searchText !== '' ? (
<ListSearch
placeHolder={`${t('Search')}...`}
searchText={searchText}
style={styles.searchStyle}
fontName={'roboto-regular'}
onSearchChange={(text) => setSearchText(text)}
onClearSearch={() => {
setSearchText('');
}}
onEndEditing={() => setSearchText(searchText)}
/>
) : null
}
/>
</PullToRefresh>
I have written custom RefreshControl by merging below 2 methods
viewablityConfig of flatlist/sectionList will help in identifying the top element of the data.
if (viewableItems[0]?.item?.url === firstCategoryUrl) {
updateIsFocusOnTopOfScreen(true);
} else {
updateIsFocusOnTopOfScreen(false);
}
After Identifying user is on top of the screen use panResponder to the flatlist/sectionList -> this is to identify the user is pulling the screen to bottom based on the this.pan.y._value increasing call your custom onRefresh method
const mover = Animated.event([null, { dx: this.pan.x, dy: this.pan.y }]);
onPanResponderMove: (e, gestureState) => {
mover(e, gestureState);
this.customRefreshControl(this.pan.y._value);
},

React Native clear text multiple TextInput boxes

I found example code on a facebook React Native page which shows how to use setNativeProp to clear text on a click but I can't see how to do it with multiple text boxes. Here is the code:
var App = React.createClass({
clearText() {
this._textInput.setNativeProps({text: ''});
},
render() {
return (
<View style={styles.container}>
<TextInput ref={component => this._textInput = component}
style={styles.textInput} />
<TouchableOpacity onPress={this.clearText}>
<Text>Clear text</Text>
</TouchableOpacity>
</View>
);
}
});
The ref seems to be fixed in the function so will always target the same TextInput box. How can I alter the function to target any TextInput box I indicate?
This should work. Notice that the ref on the TextInput needs to be the one you call from the clearText functino.
var App = React.createClass({
clearText(fieldName) {
this.refs[fieldName].setNativeProps({text: ''});
},
render() {
return (
<View style={styles.container}>
<TextInput ref={'textInput1'} style={styles.textInput} />
<TouchableOpacity onPress={() => this.clearText('textInput1')}>
<Text>Clear text</Text>
</TouchableOpacity>
<TextInput ref={'textInput2'} style={styles.textInput} />
<TouchableOpacity onPress={() => this.clearText('textInput2')}>
<Text>Clear text</Text>
</TouchableOpacity>
</View>
);
}
});
Updated my answer to clear different fields.
You can also use something like this to clear the text of TextInput.
clearText(fieldName) {
this.refs[fieldName].clear(0);
},