Opening I frame in react-native - reactjs

I'm integrating stripe payment in react-native and wants to open an iframe with specific url from after buttton click.
Scenario : user will enter the card details, details will be given to api end point and it will return an url that will contain the authentication part like OTP. So I want that url to be opened how to do so ? Let me if is there any better way to open that authentication middleware.
Adding code below
Payment.js
import React, { Component } from 'react';
import { View, Button } from 'react-native';
import stripe from 'tipsi-stripe';
import { doPayment } from '../api/api';
import { Auth } from './Auth';
stripe.setOptions({
publishableKey: 'pk_test_********',
});
export default class Payment extends Component {
state = {
isPaymentPending: false
}
requestPayment = () => {
this.setState({ isPaymentPending: true });
return stripe
.paymentRequestWithCardForm()
.then(stripeTokenInfo => {
return doPayment(10330, stripeTokenInfo.tokenId);
})
.then((res) => {
let url = "<iFrame src='" + res.intent_url + "' />"
console.log(res, url);
openAuthentication(url); --->>>> here i'm calling a function with url
})
.catch(error => {
console.warn('Payment failed', { error });
})
.finally(() => {
this.setState({ isPaymentPending: false });
});
};
render() {
return (
<View style={styles.container}>
<Button
title="Make a payment"
onPress={this.requestPayment}
disabled={this.state.isPaymentPending}
/>
</View>
);
}
}
openAuthentication = (url) => {
console.log("Here with props :::::::", url);
// here I want to open an iframe, i'm getting correct url, I've checked it in a static html page and it is working
<Auth url={url} />
}
const styles = {
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
};
Auth.js
import React, { Component } from 'react'
import {
View, StyleSheet
} from 'react-native'
import { WebView } from 'react-native-webview'
export default class Auth extends Component {
constructor(props) {
super(props)
console.log(props, ">>>>>>>>>>>")
}
render() {
console.log("In Auth -----------");
return (
<View style={styles.container}>
<WebView
source={{ uri: 'myUrl' }}
/>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
}
})
Error:
React.createElement type is invalid, expected a string or a class/function.

Auth needs to be returned by a render function, otherwise nothing will show up.
So, you'll want something similar to this:
render() {
return (
<View style={styles.container}>
<Button
title="Make a payment"
onPress={this.requestPayment}
disabled={this.state.isPaymentPending}
/>
{this.state.url && (
<Auth url={this.state.url} />
)}
</View>
);
}
You only want to render auth when you have the url.
For this, you want to update your state to look something like this:
state = {
isPaymentPending: false,
url: undefined
}
And
.then((res) => {
let url = "<iFrame src='" + res.intent_url + "' />";
this.setState({ url });
})
In order to update you state with the received url when the promise resolves. This will update your state, set the url, and re-render. Because you have an url, Auth should be rendered as well.
LE:
Your Auth.js should look something like this in order to be able to display static HTML. this.props.url should be valid HTML.
render() {
console.log("In Auth -----------");
return (
<View style={styles.container}>
<WebView
source={{ html: this.props.url }}
originWhitelist={['*']}
/>
</View>
);
}

Related

Meteor: Calling verifyEmail From React Client?

I've read many SO posts about using Accounts.onEmailVerificationLink and Accounts.verifyEmail, but I haven't yet found one that seems to explain how to call them from a React client.
I have React Router set up to route clicks on the email verification link, to a React component called ConfirmEmail. The router captures the validation token to props.match.params.emailValidationToken.
The ConfirmEmail component looks like this:
import React, {useEffect, useRef, useState} from "react";
function ConfirmEmail(props) {
const {client, match, history} = props;
const doSetUp = useRef(true);
const emailVerificationToken = useRef(props.match.params.emailValidationToken);
if (doSetUp.current){
doSetUp.current = false;
debugger;
Accounts.onEmailVerificationLink(function(token, done) {
Accounts.verifyEmail(emailVerificationToken.current, function(error) {
debugger;
if (error) {
console.log(error)
} else {
console.log(success)
}
})
});
}
return (
<p>Your email address has been verified.</p>
)
}
export default ConfirmEmail;
I've tried it a few different ways, but I haven't yet found an approach that works.
What is the correct way to handle a click on a Meteor email validation link from a React client?
Ok, I don't use React Hooks but I guess you will find easy to port it up to your flavor.
It all need to happen in componentDidMount(). This ensures that you avail of the token and that you only run this process once.
In the following code, toastr is a UI notification system for the client.
import React, { Component } from 'react'
import { connect } from 'react-redux' // if you use redux, here it is used to get the user slug from props.
import { Link } from 'react-router-dom'
import PropTypes from 'prop-types' // not relevant
import { Accounts } from 'meteor/accounts-base'
import { toastr } from 'react-redux-toastr' // not relevant
import { TiWarningOutline, TiThumbsOk } from '../../components/shared/Icons/icons' // not relevant
class ConfirmEmail extends Component {
constructor (props) {
super(props)
this.state = {
linkExpired: false,
logged: true // I need to know if the user is logged in before I test the token
}
}
componentDidMount () {
const { match, history, slug } = this.props
const token = match.params.token
// If the user is not logged in I cannot test the token against an email.
if (!slug) {
this.setState({ logged: false })
return
}
Accounts.verifyEmail(token, err => {
if (err && err.reason === 'Verify email link expired') {
this.setState({ linkExpired: true })
}
// Inform the user what went wrong
if (err) {
toastr.light('Could not verify email!', `Error: ${err.reason}`, { timeOut: 2000, icon: (<TiWarningOutline style={{ fontSize: 42, color: 'red' }} />) })
} else {
// Confirm to user and move on to where you want to direct the user first or ask the user to close the window...or else
toastr.light('You\'ve Successfully Confirmed Your Email Address!', { timeOut: 4000, icon: (<TiThumbsOk style={{ fontSize: 42, color: 'rgb(74, 181, 137)' }} />) })
history.push('/feeds')
}
})
}
render () {
const { linkExpired, logged } = this.state
return (
<div style={{ textAlign: 'center', paddingTop: 80 }}>
{logged
? <>
{linkExpired
? <p>This link has expired.</p>
: <>
<img src={`${IMAGE_BANK}/images/6.svg`} style={{ width: 36 }} /> // this is a spinner
<p>Awaiting confirmation ...</p>
</>}
</>
: <>
<p style={{ maxWidth: 360, margin: '0 auto' }}>
In order to verify your email address you need to be authenticated. Please sign in and try the verification link from your email one more time.
</p>
<br />
<Link to='/signin' className='btn btn-primary'>Sign In</Link>
</>
}
</div>
)
}
}
// this block is relevant in Redux context. Just gets the slug (or some other way to check if the user is logged in.)
const mapStateToProps = state => {
const currentUser = state.user.currentUser
return {
slug: currentUser?.slug
}
}
export default connect(mapStateToProps, { })(ConfirmEmail)
// from here, not so relevant
ConfirmEmail.propTypes = {
params: PropTypes.object
}

Refresh screen or component when navigate to it

I have two screens, one for displaying the records consuming an API and the other for registering.
the problem is that when I do a register and navigate to the display screen it does not update.
This is a construction of the screen:
constructor(props) {
super(props);
this.state = {isLoading: true, pendIsLoading: true, dataSource: [], contentStorageS:""}
};
fetchDados = async () => {
let usuario = await AsyncStorage.getItem("ASCOFAR_app_usuario");
try {
const response = await api.get("api/listaRegistros.php?usuario="+usuario);
const responseData = await response.data
if(responseData.status == "ERRO"){
this.setState({
isLoading: false,
dataSource: "",
})
}else{
this.setState({
isLoading: false,
dataSource: responseData,
})
}
console.log(response)
} catch (error) {
Alert.alert(error)
}
}
async componentDidMount () {
this.fetchDados();
this.atualizaState();
}
tirarLoad() {
if(this.state.isLoading == true){
return (
<ActivityIndicator size="large" color="#be152c"/>
)
}else if(this.state.dataSource == ""){
return (
<ScrollView >
<View style={{justifyContent:"center", alignItems:"center",}}>
<Image
style ={{width:150, height:150, marginTop:35}}
source={require('../assets/images/aguardando.png')}
/>
</View>
</ScrollView>
)
}else{
return (
<ScrollView>
<Text style={styles.tituloGrid}>Formularios Enviados</Text>
{this.state.dataSource.map(dados => (
<View style={styles.list} key={dados.id}>
<Text style={styles.listChild}>{dados.id}</Text>
<Text style={styles.listChild}>{dados.nome}</Text>
<Text>|</Text>
<Text style={styles.listChild}>{dados.endereco}</Text>
</View>
))}
</ScrollView>
)
}
}
<View style={styles.grid}>
{this.tirarLoad()}
</View>
I need to know how to do when navigating to this screen to update API consumption
Assuming you are using React-Navigation, did you try to addListener
focus react-navigation documentation
You could also do it by componentDidUpdate. I could not find the official documentation for doing it on 5.x. I believe it still works with 5.x. (Doc on 3.x)
import { withNavigationFocus } from "react-navigation";
componentDidUpdate(prevProps) {
if (prevProps.isFocused !== this.props.isFocused) {
this.fetchDados()
//or other similar onFocus function
}
}
export default withNavigationFocus(TabScreen);
Try re-rendering your Home screen after navigation
this.props.navigation.navigate('Home', {
onBack: () => this.refresh() //function to refresh screen,
});
import { withNavigationFocus } from "react-navigation";
this.willFocusSubscription = this.props.navigation.addListener(
'willFocus',
() => {
this.refreshFetch();
this.refreshLocal();
}
);
componentWillUnmount() {
this.willFocusSubscription.remove();
}

Button not displaying fetch results to the component?

I am creating a currency converter app and it will retrieve currency value from the API and multiply with the text input for the result. Both the API result and Text input are stored in State and passing as props to the Component
import React from 'react';
import { StyleSheet, Text, View,TextInput,Button } from 'react-native';
import DisplayResult from './src/DisplayResult'
export default class App extends React.Component {
state = {
currency:'',
pokeList: '',
}
placeNameChangeHandler=(val)=>{
this.setState({currency:val});
}
// console.log(this.state.currency);
async findCurrency () {
try {
//Assign the promise unresolved first then get the data using the json method.
const pokemonApiCall = await fetch('https://free.currconv.com/api/v7/convert?q=KWD_INR&compact=ultra&apiKey={my_api_Key}');
const pokemon = await pokemonApiCall.json();
this.setState({pokeList: pokemon['KWD_INR']});
// console.log(pokemon);
} catch(err) {
console.log("Error fetching data-----------", err);
};
<DisplayResult convert={this.state.pokeList} result={this.state.currency} />
}
render() {
return (
<View style={styles.container}>
<TextInput
placeholder="Currency"
value = {this.state.currency}
onChangeText={this.placeNameChangeHandler}
/>
<Button
title="Search"
onPress={this.findCurrency.bind(this)}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
DisplayResult
const DisplayResult =(props)=>{
const {convert,result} = props
console.log(convert);
return (
<View>
<Text>{result*convert}</Text>
</View>
)
}
export default DisplayResult;
I am trying to pass the API result and text input to the display component and this will multiply the values and will give the result.
Now this is not functioning or giving result
why this is not showing and where it's going wrong?
In your findCurrency method you just "call" the DisplayResult without returning it, but I don't think this is the good method to display your result.
You can use your component directly within the render method by testing your state variables, like this :
findCurrency = async () => {
try {
const pokemonApiCall = await fetch(
"https://free.currconv.com/api/v7/convert?q=KWD_INR&compact=ultra&apiKey={my_api_Key}"
);
const pokemon = await pokemonApiCall.json();
this.setState({ pokeList: pokemon["KWD_INR"] }); // You set your "pokeList" variable up
} catch (err) {
console.log("Error fetching data-----------", err);
}
}
Note that you remove the DisplayResult call here and the function became an arrowed function, then in your render method use the test to make your result appear only if pokeList isn't empty :
render() {
return (
<View style={styles.container}>
<TextInput
placeholder="Currency"
value={this.state.currency}
onChangeText={this.placeNameChangeHandler}
/>
<Button title="Search" onPress={this.findCurrency.bind(this)} />
{this.state.pokeList !== "" && (
<DisplayResult
convert={this.state.pokeList}
result={this.state.currency}
/>
)}
</View>
);
}
Then, you don't have to bind your function in the onPress method like this, JavaScript immediately calls the function if you do this, instead, use arrow functions, you can access this by doing so in your function AND the onPress method doesn't call it if you don't click on the button, you just have to specify which function to execute when clicked :
<Button title="Search" onPress={this.findCurrency} />
If you have parameters in your function, use an arrow function instead :
<Button title="Search" onPress={() => yourFunction(param)} />
This should do the trick.
Try writing your function like that :
const findCurrency = async() => {
// ...
};
and call it like that
<Button
title="Search"
onPress={() => this.findCurrency()}
/>
I personnaly never use .bind because I think this is very unclear.
try using conditional rendering,
if data fetched, then only render.
import React from 'react';
import { StyleSheet, Text, View,TextInput,Button } from 'react-native';
import DisplayResult from './src/DisplayResult'
export default class App extends React.Component {
state = {
currency: '',
pokeList: '',
}
placeNameChangeHandler=(val)=>{
this.setState({currency:val});
}
// console.log(this.state.currency);
this.findCurrency.bind(this);
async findCurrency () {
try {
//Assign the promise unresolved first then get the data using the json method.
const pokemonApiCall = await fetch('https://free.currconv.com/api/v7/convert?q=KWD_INR&compact=ultra&apiKey={my_api_Key}');
const pokemon = await pokemonApiCall.json();
this.setState({pokeList: pokemon['KWD_INR']});
// console.log(pokemon);
} catch(err) {
console.log("Error fetching data-----------", err);
};
}
render() {
return (
<View style={styles.container}>
<TextInput
placeholder="Currency"
value = {this.state.currency}
onChangeText={this.placeNameChangeHandler}
/>
<Button
title="Search"
onPress={this.findCurrency()}
/>
</View>
{
if(this.state.pokeList !== '' || this.state.currency !== '') ?
<DisplayResult convert={this.state.pokeList} result={this.state.currency} /> : <div></div>
}
);
}
}

React Component Props are receiving late. (Meteor JS)

I am working on a react-native and meteor js project.
My problem is that the props received from withTracker() function are only received in componentDidUpdate(prevProps) I don't get them in constructor or componentWillMount.
Another issue is when i pass props directly from parent to child. it receives them late due to which my component does not update
iconGroups prop comes from withTracker() method
and openSection props which i am using in this showGroupIcons()
is passed directly from parent to this component.
I want to open Accordian section that is passed to it via parent. but problem is in componentDidUpdate(prevProps) I am changing state due to which component re-renders.
openSection variable by default value is Zero. when props arrvies it value changes which i required But Accordian does not update.
Below is my code
import React, { Component } from 'react';
import Meteor, { withTracker } from 'react-native-meteor';
import {
View, Image, ScrollView, TouchableOpacity,
} from 'react-native';
import PopupDialog from 'react-native-popup-dialog';
import {Text, Icon, Input, Item, List,} from 'native-base';
import Accordion from 'react-native-collapsible/Accordion';
import { Col, Row, Grid } from 'react-native-easy-grid';
import styles from './styles';
import CONFIG from '../../config/constant';
import {MO} from "../../index";
const staticUrl = '../../assets/img/icons/';
class IconPickerComponent extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: [],
itemName: 'apple1',
activeSections: 0,
showAccordian: true,
accordianData: []
};
}
componentDidUpdate(prevProps) {
if(prevProps.iconGroups !== this.props.iconGroups) {
let images = this.props.iconGroups.map(icon => icon.images);
let flatten = [].concat.apply([], images).map(img => { return {name: img, icon: CONFIG.ICON_URL+img+'.png'} })
this.setState({ filteredItems: flatten, dataSource: flatten, accordianData: this.props.iconGroups });
}
}
componentDidMount() {
this.props.onRef(this);
}
componentWillUnmount() {
this.props.onRef(null);
}
method() {
// this.setState(...this.state,{
// searchText: ''
// })
this.iconPicker.show(); // show icon picker
}
onSearchChange(text) {
this.setState({
showAccordian: !(text.length > 0)
});
const searchText = text.toLowerCase();
const filteredItems = this.state.dataSource.filter((item) => {
const itemText = item.name.toLowerCase();
return itemText.indexOf(searchText) !== -1;
});
this.setState({ filteredItems });
}
onIconSelect(item) {
this.setState({
itemName: item,
});
this.iconPicker.dismiss();
if (this.props.onIconChanged) {
this.props.onIconChanged(item);
}
}
_renderSectionTitle = section => {
return (
<View style={styles.content}>
<Text></Text>
</View>
);
};
_renderHeader = section => {
return (
<View style={styles.accordHeader}>
<Text style={{color: 'white'}}>{this.state.showAccordian} - {section.group}</Text>
<Text>
<Icon style={styles.downArrow} name="ios-arrow-down" />
</Text>
</View>
);
};
_renderContent = section => {
return (
<View style={styles.accordContent}>
{
section.images.map((img, key) => (
<TouchableOpacity onPress={() => this.onIconSelect(img)} key={key}>
<View style={styles.iconsGrid}>
<Image style={styles.image} source={{uri: CONFIG.ICON_URL+ img + '.png'}}/>
</View>
</TouchableOpacity>
))
}
</View>
);
};
_updateSections = activeSections => {
this.setState({ activeSections });
};
hasGroupIcons() {
return this.props.iconGroups.length > 0;
};
showGroupIcons() {
if(this.state.showAccordian){
let openSection;
if(!!this.props.openSection) {
let groupIndex = this.state.accordianData.findIndex(icon => icon.group === this.props.openSection);
if(groupIndex !== -1) {
openSection = groupIndex;
} else {
openSection = 0;
}
} else {
openSection = 0;
}
return(<Accordion
sections={this.state.accordianData}
activeSections={this.state.activeSections}
renderSectionTitle={this._renderSectionTitle}
renderHeader={this._renderHeader}
renderContent={this._renderContent}
onChange={this._updateSections}
initiallyActiveSection={openSection} />);
} else {
return(<View style={{flexWrap: 'wrap', flexDirection: 'row'}}>
{
this.state.filteredItems.map((item, key) => (
<TouchableOpacity onPress={() => this.onIconSelect(item.name)} key={key}>
<View style={styles.iconsGrid}>
<Image style={styles.image} source={{uri: item.icon}}/>
</View>
</TouchableOpacity>
))
}
</View>)
}
};
render() {
return (
<PopupDialog
overlayOpacity={0.8}
overlayBackgroundColor="#414141"
dialogStyle={styles.dialogBox}
containerStyle={styles.dialogContainer}
ref={(popupDialog) => { this.iconPicker = popupDialog; }}
>
<ScrollView>
<View style={styles.dialogInner}>
<Item searchBar rounded style={styles.searchbar}>
<Icon style={styles.searchIcon} name="search" />
<Input onChangeText={this.onSearchChange.bind(this)} style={styles.inputSearch} placeholder="Search" />
</Item>
{
this.hasGroupIcons() && this.showGroupIcons()
}
</View>
</ScrollView>
</PopupDialog>
);
}
}
export default withTracker(params => {
MO.subscribe('ipSubsId3', 'IconGroups');
return {
iconGroups: MO.collection('IconGroups', 'ipSubsId3').find({}),
};
})(IconPickerComponent);
I am new to react. I am assuming when props change component re-renders.
Use this life cycle method
static getDerivedStateFromProps(prevProps, prevState) {
if(prevProps.iconGroups !== this.props.iconGroups) {
let images = this.props.iconGroups.map(icon => icon.images);
let flatten = [].concat.apply([], images).map(img => { return {name: img, icon: CONFIG.ICON_URL+img+'.png'} })
this.setState({ filteredItems: flatten, dataSource: flatten, accordianData: this.props.iconGroups });
}
}
getDerivedStateFromProps is invoked right before calling the render method, both on the initial mount and on subsequent updates. It should return an object to update the state, or null to update nothing.
Read more about this lifecycle method here
I have fixed this issue. Actually my concepts were not right. I thought props are first received in constructor and componentWillMount. But I get all props in render() and everything works fine i dont have to use any lifecycle method to use props now

React Native - Login with React Navigation

I'm struggling a bit with login functionality in my react native app.
I'm using the facebook-login, and it is working. But what to do after successful login, is where I'm struggling.
I've tried this:
To have two navigators in App.js. One for the login, and one for all the rest. In the app.js, I have a loggedIn-state which is false by default. Then when the login is successful, I want to change the loggedIn-state to true, and swap to the other navigator. But I'm unable to sending a prop from the LoginStack-navigator, to the App.js-component, so I'm not able to change the state in App.js
This is the code I'm having now:
// App.js
const RootStack = createStackNavigator({
Single: Single,
Search: Search,
Home: Homepage,
TagsSingle: TagsSingle
},
{
initialRouteName: 'Home',
drawerBackgroundColor: '#2D2D2D'
});
const LoginStack = createStackNavigator({
Login: Login,
},
{
drawerBackgroundColor: '#2D2D2D',
});
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
loggedIn: false
}
}
handleLogin = () => {
console.log("Test");
this.setState({loggedIn: true});
}
render() {
return (
<View style={{flex:1}}>{this.state.loggedIn ? <RootStack /> : <LoginStack handleLogin={this.handleLogin} />}</View>
);
}
}
// Login.js - where the login happens
class Login extends React.Component {
constructor(props){
super(props);
this.state = {
message: 'Logg inn med facebook'
}
}
_fbLogin = (ev) => {
LoginManager.logInWithReadPermissions(['public_profile']).then(function(result){
if(result.isCancelled){
console.log('Login cancelled');
}
else{
console.log('Login succes ' + result.grantedPermissions);
ev.setState({message: 'Logget inn'});
ev.props.handleLogin(); // creates error
}
}, function(error){
console.log("An error occured:");
console.log(error);
});
}
render() {
return (
<View style={{ flex:1, backgroundColor: '#2D2D2D', justifyContent: 'center', alignItems: 'center' }}>
<View>
<TouchableOpacity onPress={() => {this._fbLogin(this)}}>
<Text style={{color:'#fff'}}>{this.state.message}</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
export default Login;
But nothing happens in the HandleLogin-function in App.js, and I'm getting an error saying 'ev.props.handleLogin is not a function' (I've also tried calling the prop without '()', which gives no error, but still no reaction).
How can I solve this? Also, am I on the right track here, with the two navigators?
Try this example from their documentation.

Resources