uri should not be an empty string in React Native - javascript

I am trying to render either an image profile from firebase storage or a default avatar which is in my project img folder, depending on whether or not the current user has a profile or not. While the following code displays either the user image or the avatar image perfectly fine in my Menu.js :
import React, { Component } from 'react';
import { View, Text, Image } from 'react-native';
import { Actions } from 'react-native-router-flux';
import { Content, List, ListItem, Thumbnail } from 'native-base';
import firebase from 'firebase';
import Helpers from '../../lib/helpers';
class Menu extends Component {
constructor(props) {
super(props);
this.state = {
avatarUrl: '',
userName: '',
userLastname: '',
userEmail: '',
userBio: '',
uid: ''
};
}
async componentWillMount() {
try {
const user = await firebase.auth().currentUser;
Helpers.getImageUrl(user.uid, (imageUrl) => {
this.setState({ avatarUrl: imageUrl });
});
Helpers.getUserName(user.uid, (name) => {
this.setState({ userName: name });
});
Helpers.getUserLastname(user.uid, (lastname) => {
this.setState({ userLastname: lastname });
});
Helpers.getUserEmail(user.uid, (email) => {
this.setState({ userEmail: email });
});
Helpers.getUserBio(user.uid, (bio) => {
this.setState({ userBio: bio });
});
this.setState({ uid: user.uid });
} catch (error) {
console.log(error);
}
}
mostrarAvatar() {
if (this.state.avatarUrl) {
return (<Image
style={{ width: 100, height: 100, borderRadius: 150 / 2 }}
source={{ uri: this.state.avatarUrl }}
/>);
}
return (<Thumbnail
source={require('../../img/avatar.png')}
large
/>);
}
mostrarNombreUsuario() {
if (this.state.userName !== '') {
return <Text style={{ color: 'white', fontSize: 15 }}>{this.state.userName}</Text>;
}
return <Text style={{ color: 'white', fontSize: 15 }}>Usuario</Text>;
}
render() {
const { userName, userLastname, userEmail, userBio, avatarUrl } = this.state;
return (
<View style={{ flex: 1 }}>
<View style={{ flex: 1, backgroundColor: '#2c3e50', justifyContent: 'center', alignItems: 'center' }}>
{this.mostrarAvatar()}
{this.mostrarNombreUsuario()}
</View>
<View style={{ flex: 2 }}>
<Content>
<List>
<ListItem onPress={() => Actions.albumList()}>
<Text>Los más escuchados</Text>
</ListItem>
<ListItem>
<Text>buscar por género</Text>
</ListItem>
<ListItem onPress={() => Actions.profile({
name: userName,
lastname: userLastname,
email: userEmail,
bio: userBio,
avatar: avatarUrl })}
>
<Text>Perfil</Text>
</ListItem>
<ListItem onPress={() => Actions.auth()}>
<Text>Cerrar sesión</Text>
</ListItem>
</List>
</Content>
</View>
</View>
);
}
}
export default Menu;
The one in my Profile.js is not: (see renderAvatar function)
import React, { Component } from 'react';
import _ from 'lodash';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
Image,
TextInput,
Platform,
ToastAndroid,
Dimensions,
ScrollView,
Thumbnail
} from 'react-native';
import ImagePicker from 'react-native-image-picker';
import firebase from 'firebase';
import RNFetchBlob from 'react-native-fetch-blob';
import Helpers from '../../lib/helpers';
import Avatar from '../../img/avatar.png';
const ITEM_WIDTH = Dimensions.get('window').width;
const Blob = RNFetchBlob.polyfill.Blob;
const fs = RNFetchBlob.fs;
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
window.Blob = Blob;
const uploadImage = (uri, imageName, mime = 'image/jpg') => {
return new Promise((resolve, reject) => {
const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri;
let uploadBlob = null;
const imageRef = firebase.storage().ref('images').child(imageName);
fs.readFile(uploadUri, 'base64')
.then((data) => {
return Blob.build(data, { type: `${mime};BASE64` });
})
.then((blob) => {
uploadBlob = blob;
return imageRef.put(blob, { contentType: mime });
})
.then(() => {
uploadBlob.close();
return imageRef.getDownloadURL();
})
.then((url) => {
resolve(url);
})
.catch((error) => {
reject(error);
});
});
};
class Profile extends Component {
constructor(props) {
super(props);
this.state = {
imagePath: '',
name: this.props.avatar !== 'profile' ? this.props.name : '',
lastname: this.props.avatar !== 'profile' ? this.props.lastname : '',
email: this.props.avatar !== 'profile' ? this.props.email : '',
bio: this.props.avatar !== 'profile' ? this.props.bio : '',
uid: ''
};
}
async componentWillMount() {
try {
const { currentUser } = await firebase.auth();
this.setState({
uid: currentUser.uid
});
} catch (error) {
console.log(error);
}
}
openImagePicker() {
const options = {
title: 'Seleccione avatar',
storageOptions: {
skipBackup: true,
path: 'images'
}
};
ImagePicker.showImagePicker(options, (response) => {
if (response.didCancel) {
console.log('User canceled image Picker');
} else if (response.error) {
console.log('Error' + response.error);
} else if (response.customButton) {
console.log('User tapped custom button' + response.customButton);
} else {
this.setState({
imagePath: response.uri,
imageHeight: response.height,
imageWidth: response.width
});
}
});
}
saveForm() {
const { name, lastname, email, bio, uid, imagePath } = this.state;
if (uid) {
try {
name ? Helpers.setUserName(uid, name) : null;
bio ? Helpers.setUserBio(uid, bio) : null;
email ? Helpers.setUserEmail(uid, email) : null;
lastname ? Helpers.setUserLastname(uid, lastname) : null;
imagePath ?
uploadImage(imagePath, `${uid}.jpg`)
.then((responseData) => {
Helpers.setImageUrl(uid, responseData);
})
.done()
: null;
toastMessage('¡Tu datos han sido actualizados!');
} catch (error) {
console.log(error);
}
}
}
renderAvatar() {
console.log(this.props.avatar);
if (this.props.avatar !== null) {
return (<Image
style={{ width: 100, height: 100, borderRadius: 150 / 2 }}
source={{ uri: this.props.avatar }}
/>);
}
return (<Image
style={{ width: 100, height: 100, borderRadius: 150 / 2 }}
source={Avatar}
/>);
}
render() {
return (
<ScrollView contentContainerStyle={{ alignItems: 'center' }} style={styles.container}>
<TouchableOpacity
onPress={this.openImagePicker.bind(this)}
style={{ marginBottom: 40, marginTop: 20 }}
>
<View style={{ alignItems: 'center' }}>
{this.renderAvatar()}
<Text style={{ color: 'white' }}>Avatar </Text>
</View>
</TouchableOpacity>
<TextInput
underlineColorAndroid='rgba(0,0,0,0)'
selectionColor="#fff"
placeholderTextColor="#ffffff"
autoCorrect={false} //we disable the autocorrect from ios or android
style={styles.textInput}
placeholder='nombres'
value={this.state.name}
onChangeText={(name) => this.setState({ name })}
/>
<TextInput
underlineColorAndroid='rgba(0,0,0,0)'
selectionColor="#fff"
placeholderTextColor="#ffffff"
autoCorrect={false} //we disable the autocorrect from ios or android
style={styles.textInput}
placeholder='apellidos'
value={this.state.lastname}
onChangeText={(lastname) => this.setState({ lastname })}
/>
<TextInput
underlineColorAndroid='rgba(0,0,0,0)'
selectionColor="#fff"
placeholderTextColor="#ffffff"
autoCorrect={false} //we disable the autocorrect from ios or android
style={styles.textInput}
type='email-address'
placeholder='email#gmail.com'
keyboardType="email-address"
autoCapitalize="none"
value={this.state.email}
onChangeText={(email) => this.setState({ email })}
/>
<TextInput
multiline={true}
numberOfLines={10}
underlineColorAndroid='rgba(0,0,0,0)'
selectionColor="#fff"
placeholderTextColor="#ffffff"
autoCorrect={false} //we disable the autocorrect from ios or android
style={styles.textArea}
placeholder='Ingresa algo sobre tí'
value={this.state.bio}
onChangeText={(bio) => this.setState({ bio })}
/>
<TouchableOpacity
style={styles.buttonStyle}
onPress={this.saveForm.bind(this)}
>
<Text style={styles.buttonText}>Guardar</Text>
</TouchableOpacity>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#0277BD',
},
textInput: {
height: 40,
borderWidth: 1,
borderColor: '#cecece',
marginBottom: 20,
borderRadius: 20,
paddingHorizontal: 20,
marginHorizontal: 50,
backgroundColor: 'rgba(255, 255, 255, 0.3)',
color: '#ffffff',
width: ITEM_WIDTH - 100,
},
buttonStyle: {
justifyContent: 'center',
alignItems: 'center',
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 20,
paddingVertical: 10,
width: 100,
backgroundColor: '#2D09A1',
marginHorizontal: 20,
marginTop: 20,
marginBottom: 20
},
buttonText: {
color: 'white',
fontSize: 16,
fontWeight: '600',
},
textArea: {
height: 150,
justifyContent: 'flex-start',
textAlignVertical: 'top',
borderWidth: 1,
borderColor: '#cecece',
marginBottom: 20,
borderRadius: 20,
paddingHorizontal: 16,
backgroundColor: 'rgba(255, 255, 255, 0.3)',
color: '#ffffff',
width: ITEM_WIDTH - 150,
}
});
const toastMessage = (texto) => {
ToastAndroid.showWithGravity(
texto,
ToastAndroid.SHORT,
ToastAndroid.CENTER
);
};
export default Profile;
The problem is in the renderAvatar method: I have defined the method in the same way that I did in the Menu.js and called it in the return the same way I did in the Menu.js and used the same image path as in the Menu.js, and both the Menu.js and Profile.js are located in the same directory. But I get the following error: 'uri should not be an empty string'.
When I just return:
render() {
return (
<ScrollView contentContainerStyle={{ alignItems: 'center' }} style={styles.container}>
<TouchableOpacity
onPress={this.openImagePicker.bind(this)}
style={{ marginBottom: 40, marginTop: 20 }}
>
<View style={{ alignItems: 'center' }}>
<Image
style={{ width: 100, height: 100, borderRadius: 150 / 2 }}
source={Avatar}
/> ...
in my Profile.js the default avatar image displays correctly. I don't get this. I don't get the logic of this. If anybody can help me I would appreciate it. Thanks.

In the constructor you can not use this.props. you have to use props directly because this is not available at that moment. Let me see your code when you use the Profile component.
I think you have to compare this.props.avatar with '' not with null because your initial state.
<ListItem onPress={() => Actions.profile({
name: userName,
lastname: userLastname,
email: userEmail,
bio: userBio,
avatar: avatarUrl })}
>
you are passing '' not null in avatarUrl value. Also you can initialize the avatarUrl state with the uri value of the default avatar you have until this change, because of this your Profile component always have a valid url for the image component.

Related

OnPress that redirect to other page

I am trying to develop an application to learn react native and I am stuck with the login/register.
I have a register.js that works properly, as I have tested it before, and I wanted to add a resiter button to redirect to the register page from the login one.
From the UserLogin.js, the button is this one:
<TouchableOpacity
onPress={() => navigate('Register')}
style={{width:200,padding:10,backgroundColor:'pink',alignItems:'center'}}>
<Text style={{color:'black'}}>Register</Text>
</TouchableOpacity>
UserLogin.js
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,TouchableOpacity,TextInput,Button,Keyboard
} from 'react-native';
import { StackNavigator } from 'react-navigation';
class UserLogin extends React.Component {
constructor(props){
super(props)
this.state={
userEmail:'',
userPassword:''
}
}
login = () =>{
const {userEmail,userPassword} = this.state;
let reg = /^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/ ;
if(userEmail==""){
//alert("Please enter Email address");
this.setState({email:'Please enter Email address'})
}
else if(reg.test(userEmail) === false)
{
//alert("Email is Not Correct");
this.setState({email:'Email is Not Correct'})
return false;
}
else if(userPassword==""){
this.setState({email:'Please enter password'})
}
else{
fetch('http://192.168.1.47/login.php',{
method:'post',
header:{
'Accept': 'application/json',
'Content-type': 'application/json'
},
body:JSON.stringify({
// we will pass our input data to server
email: userEmail,
password: userPassword
})
})
.then((response) => response.json())
.then((responseJson)=>{
if(responseJson == "ok"){
// redirect to profile page
alert("Successfully Login");
//this.props.navigation.navigate("Profile");
}else{
alert("Wrong Login Details");
}
})
.catch((error)=>{
console.error(error);
});
}
Keyboard.dismiss();
}
render() {
const { navigate } = this.props.navigation;
return (
<View style={styles.container}>
<Text style={{padding:10,margin:10,color:'red'}}>{this.state.email}</Text>
<TextInput
placeholder="Enter Email"
style={{width:200, margin:10}}
onChangeText={userEmail => this.setState({userEmail})}
/>
<TextInput
placeholder="Enter Password"
style={{width:200, margin:10}}
onChangeText={userPassword => this.setState({userPassword})}
/>
<TouchableOpacity
onPress={this.login}
style={{width:200,padding:10,backgroundColor:'magenta',alignItems:'center'}}>
<Text style={{color:'white'}}>Login</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => navigate('Register')}
style={{width:200,padding:10,backgroundColor:'pink',alignItems:'center'}}>
<Text style={{color:'black'}}>Register</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
});
export default UserLogin
AppRegistry.registerComponent('login', () => login);
register.js
import React, { Component } from 'react';
import { View, Text, StyleSheet, TextInput, TouchableOpacity, ActivityIndicator, Platform } from 'react-native';
class Register extends React.Component
{
constructor()
{
super();
this.state = { first_name: '', last_name: '', email: '', password: '', loading: false, disabled: false }
}
saveData = () =>
{
this.setState({ loading: true, disabled: true }, () =>
{
fetch('http://192.168.1.47/user_registration.php',
{
method: 'POST',
headers:
{
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
first_name: this.state.first_name,
last_name: this.state.last_name,
email: this.state.email,
password: this.state.password,
})
}).then((response) => response.json()).then((responseJson) =>
{
//Faire une redirection evrs le profile de l'utilisateur
alert(responseJson);
this.setState({ loading: false, disabled: false });
}).catch((error) =>
{
console.error(error);
this.setState({ loading: false, disabled: false });
});
});
}
render()
{
return(
<View style = { styles.container }>
<TextInput underlineColorAndroid = "transparent" placeholder = "Your First Name" style = { styles.textInput } onChangeText = {(text) => this.setState({ first_name: text })}/>
<TextInput underlineColorAndroid = "transparent" placeholder = "Your Last Name" style = { styles.textInput } onChangeText = {(text) => this.setState({ last_name: text })}/>
<TextInput underlineColorAndroid = "transparent" placeholder = "Your Email adress" style = { styles.textInput } onChangeText = {(text) => this.setState({ email: text })}/>
<TextInput underlineColorAndroid = "transparent" placeholder = "Your Password" style = { styles.textInput } onChangeText = {(text) => this.setState({ password: text })}/>
<TouchableOpacity disabled = { this.state.disabled } activeOpacity = { 0.8 } style = { styles.Btn } onPress = { this.saveData }>
<Text style = { styles.btnText }>Sign up</Text>
</TouchableOpacity>
{
(this.state.loading)
?
(<ActivityIndicator size = "large" />)
:
null
}
</View>
);
}
}
const styles = StyleSheet.create(
{
container:
{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#eee',
paddingHorizontal: 25,
paddingTop: (Platform.OS == 'ios') ? 20 : 0
},
textInput:
{
height: 40,
borderWidth: 1,
borderColor: 'grey',
marginVertical: 5,
alignSelf: 'stretch',
padding: 8,
fontSize: 16
},
Btn:
{
backgroundColor: 'rgba(0,0,0,0.6)',
alignSelf: 'stretch',
padding: 10,
marginTop: 10,
marginBottom: 25
},
btnText:
{
textAlign: 'center',
color: 'white',
fontSize: 16
}
});
export default Register
Navigation.js
import Search from '../Components/Search';
import ProductDetail from '../Components/ProductDetail';
//import UserAccount from '../Components/UserAccount';
import UserLogin from '../Components/UserLogin';
//import UserRegistration from '../Components/UserRegistration';
import Transaction from '../Components/Transaction';
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import React from 'react';
import { Image , Text, TouchableOpacity } from 'react-native';
const SearchStackNavigator = createStackNavigator({
Rechercher : {
screen: Search,
navigationOptions: ({ navigate, navigation }) => ({
title:'Rechercher',
headerTitleStyle: {
fontWeight: 'bold',
color: 'white',
marginLeft:5,
},
headerStyle: {
backgroundColor: '#FF6100',
},
headerLeft: (
<TouchableOpacity
onPress={() => navigation.navigate('Rechercher')}>
<Image
style={{
width: 35,
height: 35,
marginLeft: 25,
}}
source={require('../Images/logo.jpg')}
/>
</TouchableOpacity>
),
headerRight: (
<TouchableOpacity
onPress={() => navigation.navigate('Account')}>
<Image
style={{
width: 35,
height: 35,
marginRight: 25,
}}
source={require('../Images/user.png')}
/>
</TouchableOpacity>
)
})
},
ProductDetail : {
screen: ProductDetail,
navigationOptions: ({ navigate, navigation }) => ({
title:'Détail du produit',
headerTitleStyle: {
fontWeight: 'bold',
color: 'white',
marginLeft:5,
},
headerStyle: {
backgroundColor: '#FF6100',
},
headerLeft: (
<TouchableOpacity
onPress={() => navigation.navigate('Rechercher')}>
<Image
style={{
width: 35,
height: 35,
marginLeft: 25,
}}
source={require('../Images/logo.jpg')}
/>
</TouchableOpacity>
),
headerRight: (
<TouchableOpacity
onPress={() => navigation.navigate('Account')}>
<Image
style={{
width: 35,
height: 35,
marginRight: 25,
}}
source={require('../Images/user.png')}
/>
</TouchableOpacity>
)
})
},
Account: {
//screen: UserRegistration,
screen: UserLogin,
navigationOptions: ({ navigate, navigation }) => ({
title:'Compte utilisateur',
headerTitleStyle: {
fontWeight: 'bold',
color: 'white',
marginLeft:5,
},
headerStyle: {
backgroundColor: '#FF6100',
},
headerLeft: (
<TouchableOpacity
onPress={() => navigation.navigate('Rechercher')}>
<Image
style={{
width: 35,
height: 35,
marginLeft: 25,
}}
source={require('../Images/logo.jpg')}
/>
</TouchableOpacity>
),
headerRight: (
<TouchableOpacity
onPress={() => navigation.navigate('Account')}>
<Image
style={{
width: 35,
height: 35,
marginRight: 25,
}}
source={require('../Images/user.png')}
/>
</TouchableOpacity>
)
})
},
Transaction: {
screen: Transaction,
navigationOptions: ({ navigate, navigation }) => ({
title:'Transaction',
headerTitleStyle: {
fontWeight: 'bold',
color: 'white',
marginLeft:5,
},
headerStyle: {
backgroundColor: '#FF6100',
},
headerLeft: (
<TouchableOpacity
onPress={() => navigation.navigate('Rechercher')}>
<Image
style={{
width: 35,
height: 35,
marginLeft: 25,
}}
source={require('../Images/logo.jpg')}
/>
</TouchableOpacity>
),
headerRight: (
<TouchableOpacity
onPress={() => navigation.navigate('Account')}>
<Image
style={{
width: 35,
height: 35,
marginRight: 25,
}}
source={require('../Images/user.png')}
/>
</TouchableOpacity>
)
})
},
})
export default createAppContainer(SearchStackNavigator)
Navigation.js is located on a folder named Navigation.
The two other files are on the same folder (The components one).
Can someone help me fixe it ? I don't think it's complicated, but there is something I didn't understand, I think.
Thank you in advance.
Noémie.
Your Register, and Login screens have to be listed as routes in your
stack-navigation ... so you can get access to navigation object to use
it for navigation – Hend El-Sahli

Error: signInWithPhoneNumber failed: Seconde argument "ApplicationVerifier" must be an implementation of firebase.auth.ApplicationVerifier

I'm working on graduation project, trying to create signIn with phone number Interface with React Native,
I found some codes for SMS Verification here : https://www.instamobile.io/mobile-development/firebase-phone-authentication-react-native/ .... But it gives me an Error !! " Error: signInWithPhoneNumber failed: Seconde argument "ApplicationVerifier" must be an implementation of firebase.auth.ApplicationVerifier"
I tried to connect Xcode with the app by ULR Scheme But its still not working!
Here is my code Hope Can HeLp ME :
import React, { Component } from 'react';
import {
StyleSheet,
SafeAreaView,
TouchableOpacity,
View,
Text,
TextInput
} from 'react-native';
//import Firebase from '../components/Firebase'
import {Actions} from 'react-native-router-flux';
import * as firebase from 'firebase';
const Config = {
apiKey: "AIzaSyAsT6g2R1lXZb8soYZB9MtbM**********",
authDomain: "********application.firebaseapp.com",
databaseURL: "https://********application.firebaseio.com",
projectId: "********application",
storageBucket: "********application.appspot.com",
messagingSenderId: "*****102252",
appId: "1:35406102252:web:8c19bfa513e6**********",
measurementId: "G-V4S6YST***"
};
firebase.initializeApp(Config);
class PhoneAuthScreen extends Component {
state = {
phone: '',
confirmResult: null,
verificationCode: '',
userId: '',
}
validatePhoneNumber = () => {
var regexp = /^\+[0-9]?()[0-9](\s|\S)(\d[0-9]{8,16})$/
return regexp.test(this.state.phone)
}
handleSendCode = () => {
var appVerifier = window.recaptchaVerifier;
// Request to send OTP
if (this.validatePhoneNumber()) {
firebase
.auth()
.signInWithPhoneNumber(this.state.phone, appVerifier)
.then(confirmResult => {
this.setState({ confirmResult })
})
.catch(error => {
alert(error.message)
console.log(error)
})
} else {
alert('Invalid Phone Number')
}
this.setState({ confirmResult: null, verificationCode: '' })
}
handleVerifyCode = () => {
// Request for OTP verification
const { confirmResult, verificationCode } = this.state
if (verificationCode.length == 6) {
confirmResult
.confirm(verificationCode)
.then(user => {
this.setState({ userId: user.uid })
alert(`Verified! ${user.uid}`)
})
.catch(error => {
alert(error.message)
console.log(error)
})
} else {
alert('Please enter a 6 digit OTP code.')
}
}
renderConfirmationCodeView = () => {
return (
<View style={styles.verificationView}>
<TextInput
style={styles.textInput}
placeholder='Verification code'
placeholderTextColor='#eee'
value={this.state.verificationCode}
keyboardType='numeric'
onChangeText={verificationCode => {
this.setState({ verificationCode })
}}
maxLength={6}
/>
<TouchableOpacity
style={[styles.themeButton, { marginTop: 20 }]}
onPress={this.handleVerifyCode}>
<Text style={styles.themeButtonTitle}>Verify Code</Text>
</TouchableOpacity>
</View>
)
}
render() {
return (
<SafeAreaView style={[styles.container, { backgroundColor: '#333' }]}>
<View style={styles.page}>
<TextInput
style={styles.textInput}
placeholder='Phone Number with country code'
placeholderTextColor='#eee'
keyboardType='phone-pad'
value={this.state.phone}
onChangeText={phone => {
this.setState({ phone })
}}
maxLength={15}
editable={this.state.confirmResult ? false : true}
/>
<TouchableOpacity
style={[styles.themeButton, { marginTop: 20 }]}
onPress={
this.state.confirmResult
? this.changePhoneNumber
: this.handleSendCode
}>
<Text style={styles.themeButtonTitle}>
{this.state.confirmResult ? 'Change Phone Number' : 'Send Code'}
</Text>
</TouchableOpacity>
{this.state.confirmResult ? this.renderConfirmationCodeView() : null}
</View>
</SafeAreaView>
)
};
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#aaa'
},
page: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
},
textInput: {
marginTop: 20,
width: '90%',
height: 40,
borderColor: '#555',
borderWidth: 2,
borderRadius: 5,
paddingLeft: 10,
color: '#fff',
fontSize: 16
},
themeButton: {
width: '90%',
height: 50,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#888',
borderColor: '#555',
borderWidth: 2,
borderRadius: 5
},
themeButtonTitle: {
fontSize: 24,
fontWeight: 'bold',
color: '#fff'
},
verificationView: {
width: '100%',
alignItems: 'center',
marginTop: 50
}
})
export default PhoneAuthScreen;
firebase.auth().signInWithPhoneNumber(phoneNumber)
.then(confirmResult => // save confirm result to use with the manual verification code)
.catch(error => /error);
No need to add appVerifier.
Refer to the official documentation
https://rnfirebase.io/docs/v5.x.x/auth/phone-auth
Here.

How to show error message on react native using redux

i am using redux in my react native app .
my problem is how to show the error message that appear in the terminal to the render .
i tried to use {this.props.error} and nothing will show up
Actions
import * as actionTypes from "./Types";
export const setErrors = errors => ({
type: actionTypes.SET_ERRORS,
payload: errors
});
export const resetError = () => {
return async dispatch => {
dispatch({
type: actionTypes.RESET_ERROR,
payload: []
});
};
};
Reducers
import * as actionTypes from "../actions/Types";
import { SET_ERRORS, RESET_ERROR } from "../actions/Types";
const initialState = {
errors: [],
reset: ""
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case SET_ERRORS:
console.log("from reducer");
console.log("errrrro", action.payload);
return {
...state,
errors: Object.keys(action.payload).map(
key => `${key}: ${action.payload[key]}`
)
};
case RESET_ERROR:
return {
...state,
errors: action.payload
};
default:
return state;
}
};
export default reducer;
this is my index.js
import React, { Component } from "react";
import { connect } from "react-redux";
import * as actionCreators from "../../store/actions";
import {
StyleSheet,
Text,
View,
TextInput,
Button,
TouchableHighlight,
Image
} from "react-native";
import { LinearGradient } from "expo-linear-gradient";
class Login extends Component {
state = {
username: "",
password: ""
};
resetState = () => {
this.setState({ password: null });
};
errors = () => {
this.props.errors;
};
Login = () => {
this.props.login(this.state, this.props.navigation);
this.resetState();
};
render() {
return (
<LinearGradient
colors={["#002d44", "#001724"]}
style={{ flex: 1 }}
start={[0, 0]}
end={[1, 0]}
>
<View style={styles.container}>
<Text>{error}</Text>
<View style={styles.inputContainer}>
<Image
style={styles.inputIcon}
source={{
uri:
"https://img.icons8.com/ios/80/000000/identification-documents-filled.png"
}}
/>
<TextInput
style={styles.inputs}
autoCapitalize="none"
placeholder="اسم المستخدم"
onChangeText={username => this.setState({ username })}
value={this.state.username}
/>
</View>
<View style={styles.inputContainer}>
<Image
style={styles.inputIcon}
source={{
uri: "https://img.icons8.com/ios/80/000000/lock-2-filled.png"
}}
/>
<TextInput
style={styles.inputs}
placeholder="كلمة المرور"
secureTextEntry={true}
onChangeText={password => this.setState({ password })}
value={this.state.password}
/>
</View>
<TouchableHighlight
style={[styles.buttonContainer, styles.loginButton]}
onPress={this.Login}
>
<Text style={{ color: "#fff", fontSize: 15 }}>دخول</Text>
</TouchableHighlight>
<TouchableHighlight
style={{}}
onPress={() => this.props.navigation.replace("Signup")}
>
<Text style={{ color: "#00F7EA", fontSize: 15 }}>تسجيل</Text>
</TouchableHighlight>
</View>
</LinearGradient>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center"
},
inputContainer: {
borderBottomColor: "#F5FCFF",
backgroundColor: "#FFFFFF",
borderRadius: 30,
borderBottomWidth: 1,
width: 250,
height: 45,
marginBottom: 20,
flexDirection: "row",
alignItems: "center"
},
inputs: {
height: 45,
marginLeft: 16,
fontSize: 15,
textAlign: "center",
borderBottomColor: "#FFFFFF",
flex: 1
},
inputIcon: {
width: 30,
height: 30,
marginLeft: 15,
justifyContent: "center"
},
buttonContainer: {
height: 45,
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
marginBottom: 20,
width: 250,
borderRadius: 30,
borderWidth: 0.8,
borderColor: "#d6d7da"
},
loginButton: {
backgroundColor: "transparent"
},
buttonDisabled: {
backgroundColor: "#e7e7e7"
},
loginText: {
color: "white"
}
});
const mapStateToProps = state => ({
user: state.authReducer.user,
loading: state.devicesReducer.loading,
errors: state.errorReducer.errors
});
const mapDispatchToProps = dispatch => ({
login: (userData, navigation) =>
dispatch(actionCreators.login(userData, navigation)),
checkForExpiredToken: navigation =>
dispatch(actionCreators.checkForExpiredToken(navigation))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(Login);
the error message that showing on the terminal
from reducer
errrrro Object {
"password": Array [
"This field may not be blank.",
],
"username": Array [
"This field may not be blank.",
],
}
from reducer
errrrro [Error: Request failed with status code 400]

How to update the state after refresh the App - React Native?

I'm development some login screen with a simple Welcome screen,
but I have some issue with the state when refreshing the app it's still in the splash screen [Indicator] not separate,
I need when I'm removing the storage key update the state loading into another status but it doesn't work with me :L, can you help me about this issue?
myScreens
After first Login
When refreshing the app [double R key] - I need moving me to the Login form
this my Code
/Login.js
import React, { Component } from "react";
import {
StyleSheet,
View,
StatusBar,
Text,
TextInput,
TouchableOpacity,
ActivityIndicator,
AsyncStorage
} from "react-native";
export default class Login extends Component {
constructor() {
super();
this.state = {
username: "",
password: "",
isLoad: false
};
}
async componentWillMount() {
await AsyncStorage.getItem("#myApp:username").then(username => {
if (!username || username === "") {
this.setState({
isLoad: true
});
} else {
this.setState({ username: username });
this.props.navigation.navigate("Home");
}
});
}
_onLogin = () => {
let { username, password } = this.state;
if (username !== "" && password !== "") {
AsyncStorage.setItem("#myApp:username", this.state.username).then(
username => {
this.setState({ username: username });
}
);
this.props.navigation.navigate("Home");
}
};
render() {
if (!this.state.isLoad) {
return (
<View
style={{ flex: 1, justifyContent: "center", alignItems: "center" }}
>
<ActivityIndicator size="large" color="red" />
</View>
);
} else {
return (
<View style={styles.container}>
<StatusBar backgroundColor="#333" barStyle="light-content" />
<Text style={{ fontSize: 28, margin: 10 }}>Login</Text>
<TextInput
placeholder="Username"
onChangeText={username => {
this.setState({ username: username });
}}
value={this.state.username}
autoCorrect={false}
returnKeyType="next"
style={{
padding: 10,
margin: 15,
borderBottomColor: "#333",
borderBottomWidth: 1,
width: "80%"
}}
editable={true}
/>
<TextInput
placeholder="Password"
onChangeText={password => {
this.setState({ password: password });
}}
secureTextEntry={true}
value={this.state.password}
autoCorrect={false}
returnKeyType="next"
style={{
padding: 10,
margin: 15,
borderBottomColor: "#333",
borderBottomWidth: 1,
width: "80%"
}}
editable={true}
/>
<TouchableOpacity
style={{
margin: 20,
padding: 10,
width: "70%",
backgroundColor: "#1a73e8"
}}
onPress={this._onLogin}
>
<Text style={{ color: "#FFF", textAlign: "center", fontSize: 18 }}>
Log In
</Text>
</TouchableOpacity>
</View>
);
}
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#fff"
}
});
./Home.js
import React, { Component } from "react";
import {
StyleSheet,
View,
ImageBackground,
Alert,
Text,
AsyncStorage,
TouchableOpacity
} from "react-native";
export default class Home extends Component {
constructor() {
super();
this.state = {
name: ""
};
}
async componentDidMount() {
try {
const storedValue = await AsyncStorage.getItem("#myApp:username");
this.setState({ name: storedValue });
if (storedValue == null) {
this.props.navigation.navigate("Login");
}
} catch (error) {
Alert.alert("Error", "There was an error.");
}
}
_onLogout = () => {
AsyncStorage.removeItem("#myApp:username").then(() => {
this.setState({ name: "" });
});
this.props.navigation.navigate("Login");
};
render() {
return (
<View style={styles.container}>
<ImageBackground
source={require("./assets/bg1.jpg")}
style={{
flex: 1,
width: "100%",
height: "100%"
}}
>
<View
style={{
backgroundColor: "rgba(0,0,0,0)",
flex: 1,
justifyContent: "center"
}}
>
<Text
style={{
alignSelf: "center",
backgroundColor: "#000fff",
padding: 10,
color: "#fff"
}}
>
{`Hello, ${this.state.name} `}
</Text>
<TouchableOpacity
style={{
margin: 20,
padding: 10,
width: "50%",
backgroundColor: "#1a73e8",
justifyContent: "center",
alignSelf: "center"
}}
onPress={this._onLogout}
>
<Text
style={{ color: "#FFF", textAlign: "center", fontSize: 18 }}
>
Log out?
</Text>
</TouchableOpacity>
</View>
</ImageBackground>
</View>
);
}
}
The problem is in your componentWillMount
async componentWillMount() {
await AsyncStorage.getItem("#myApp:username").then(username => {
if (!username || username === "") {
this.setState({
isLoad: true // This is the issue
});
} else {
this.setState({ username: username });
this.props.navigation.navigate("Home");
}
});
}
First of all, you should avoid using componentWillMount and use componentDidMount as the former is now deprecated, and is not advised to use in the newer versions of react.
The problem is, when you navigate to the home screen after logging out, you are checking for username in the AsyncStorage but if its undefined, you're setting the isLoad state to true, which renders the ActivityIndicator. You should be doing isLoad before the promise is resolved if anything. If you replace your code with:
componentDidMount() {
this.setState({ isLoad: true });
AsyncStorage.getItem("#myApp:username").then(username => {
if (!username || username === "") {
this.setState({
isLoad: false // change this to False
});
} else {
this.setState({ username: username, isLoad: false });
this.props.navigation.navigate("Home");
}
});
}
You should be able to see a login Screen instead of the spinner. Hope this helps.

How to view multiple capture image horizontally in react native?

I just want to capture image using camera API
Then Store it in array
then view one by one image horizontally in React native
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
View,
TouchableOpacity,
Image,
PixelRatio
} from 'react-native';
import ImagePicker from 'react-native-image-picker';
let ret;
export default class Location extends Component {
constructor(props){
super(props);
this.state = {
latitude: null,
longitude: null,
address: 'address',
MyAddress: null,
error: null,
};
}
state = {
avatarSource: null,
videoSource: null
};
selectPhotoTapped() {
const options = {
quality: 1.0,
maxWidth: 500,
maxHeight: 500,
storageOptions: {
skipBackup: true
}
};
To get Image, I used this.
ImagePicker.showImagePicker(options, (response) => {
console.log('Response = ', response);
if (response.didCancel) {
console.log('User cancelled photo picker');
}
else if (response.error) {
console.log('ImagePicker Error: ', response.error);
}
else if (response.customButton) {
console.log('User tapped custom button: ', response.customButton);
}
else {
let source = { uri: response.uri };
// You can also display the image using data:
//let source = { uri: 'data:image/jpeg;base64,' + response.data };
this.setState({
avatarSource: source
});
}
});
}
Then how to process to view list of image like adpater.
return (
<View style={{ flexGrow: 1, alignItems: 'center', justifyContent: 'center' }}>
To show the image in one view ,i have done this. i want to view multiple image like view pager.Like [] [] [] []
<TouchableOpacity onPress={this.selectPhotoTapped.bind(this)}>
<View style={[styles.avatar, styles.avatarContainer, {marginBottom: 20}]}>
{ this.state.avatarSource === null ? <Text>Select a Photo</Text> :
<Image style={styles.avatar} source={this.state.avatarSource} />
}
</View>
</TouchableOpacity>
</View>
)
}
}
After few hours i have done this
First save image uri in array then show in list..
let dataStorage = [{uri: response.uri}, ...this.state.dataStorage]
this.setState({dataStorage})
<FlatList
horizontal
data={this.state.dataStorage}
renderItem={({ item: rowData }) => {
return (
<View style={[styles.avatar, styles.avatarContainer, {marginBottom: 20}]}>
{ <Image style={styles.avatar} source={{uri:rowData.uri}} /> }
</View>
);
}
}
keyExtractor={(item, index) => index}
/>
Using Expo's ImagePicker this can be achieved.
Below is my code that might help you.
Note: Its performance is slow but it works.
1) state:
constructor(props) {
super(props);
this.state = {
images: []
};
}
2) openCamera function:
openCamera = async () => {
const { status } = await Permissions.askAsync(
Permissions.CAMERA,
Permissions.CAMERA_ROLL
);
this.setState({
hasCameraPermission: status === 'granted',
hasGalleryPermission: status === 'granted'
});
if (this.state.hasCameraPermission && this.state.hasGalleryPermission) {
ImagePicker.launchCameraAsync({
aspect: [1, 1],
quality: 0.2,
base64: true
})
.then(async result => {
if (!result.cancelled) {
await this.setState({
images: this.state.images.concat(result.uri)
});
Alert.alert(
'Add another picture? ',
null,
[
{
text: 'Yes',
onPress: () => this.openCamera()
},
{ text: 'No' }
],
{ cancelable: false }
);
}
console.log(this.state.images);
})
.catch(error => console.log(error));
}
};
3) To display Image:
showImages = () => {
let temp_image = [];
this.state.images.map((item, index) => {
let tempKey = item + '123';
temp_image.push(
<View key={tempKey}>
<View
key={index}
style={{
height: 100,
width: 100,
borderColor: '#dddddd'
}}
>
<Image
key={index}
source={{ uri: item }}
style={{
flex: 1,
flexDirection: 'row',
backgroundColor: 'silver',
padding: 5,
borderRadius: 5,
height: null,
width: null,
margin: 3,
resizeMode: 'cover'
}}
/>
</View>
<View key={index + 1}>
<TouchableOpacity
key={index + 2}
style={{
flex: 1,
justifyContent: 'center',
alignSelf: 'center',
width: '100%'
}}
onPress={() => {
this.removeImage(index);
this.forceUpdate();
}}
>
<Text
key={index + 3}
style={{
alignSelf: 'center',
color: '#CE3C3E',
fontWeight: 'bold'
}}
>
Delete
</Text>
</TouchableOpacity>
</View>
</View>
);
});
console.log('state images: ', this.state.images);
return temp_image;
};
4) To delete image:
removeImage = index => {
Alert.alert('Delete this image?', null, [
{
text: 'Yes',
onPress: () => {
this.state.images.splice(index, 1);
// console.log(this.state.images);
this.forceUpdate();
}
},
{ text: 'No' }
]);
};
5) And in render() function:
<ScrollView horizontal={true} style={{ flex: 1, flexDirection: 'row' }} >
{this.showImages()}
</ScrollView>

Categories

Resources