How to Subscribe to Platform Event Notifications with React Native - react-native

I am developing a mobile application with React Native. With this application, I connect to salesforce and make transactions.
I created Platform Events on Salesforce side and I want React Native Component to subscribe to it.
I can't use lightning/empApi or CometD because I don't code web. According to the document, my only option is Pub/Sub API.
I tried to pull it directly with FETCH but with no result.
import {View, Text} from 'react-native';
import React, { useEffect, useState } from 'react';
import { Force, oauth } from 'react-native-force';
function History() {
const [session, setSession] = useState()
const eventName = "Test_PE__e";
const replayId = -1;
const [subscription, setSubscription] = React.useState();
useEffect(() => {
oauth.getAuthCredentials(
(data) => console.log(data), // already logged in
() => {
oauth.authenticate(
() => setSession(data),
(error) => console.log('Failed to authenticate:' + error)
);
});
},[]);
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text> Subscribed to {eventName} </Text>
</View>
);
async function setPEvents(session) {
const headers = new Headers({
'Authorization': `Bearer ${session.accessToken}`,
'Content-Type': 'application/json'
});
const body = {
"event": `/event/${eventName}`,
"replayId": replayId
};
const url = `${session.instanceUrl}/services/data/v57.0/event/${eventName}/subscriptions`;
const requestOptions = {
method: 'POST',
headers: headers,
body: JSON.stringify(body)
};
const resp = await fetch(url, requestOptions)
.then(async (response) => {
console.log(response);
return await response.json();
})
.catch(error => console.log('Error:', error));
setSubscription(resp);
console.log("FIRATTT");
console.log("Result:",resp);
}
}
export default History;
What can I do to subscribe?
Or how can I use the SalesForce Pub/Sub API in React Native?

Related

how to set session auth token in react native

I am trying to set up a session in my react native
I am new to mobile app development. I don't know whether this is the correct approach to set a session
I am coming from react js background so I tried this approach but in the application tab in react native debugger I dont find the session token set can someone explain and help me whether this is a correct approach
axios({
method: 'POST',
url: 'http://127.0.0.1:8000/api/register',
data: Data56,
});
.then(function (response) {
storeData(response.data.token);
alert('sucess');
.catch(error => {
alert(JSON.stringify(error.response, 'catch'));
});
}
first install
npm install react-client-session --save
Then
import { ReactSession } from 'react-client-session';
function Login() {
ReactSession.setStoreType("localStorage");
ReactSession.set("username", "Meon");
return (
<div>
<Switch>
// Routes
</Switch>
</div>
);
}
export default Login;
Then Call your session anywhere as like this
const loggedIn = ReactSession.get("username");
You can use #react-native-async-storage/async-storage library for storing your AUTH_TOKEN. Here is the code with explanation.
When user login your api would return a token that you can store the in the AysncStorage.
const storeData = async (value) => {
try {
await AsyncStorage.setItem('auth_token', value)
} catch (e) {
// saving error
}
}
and with this function you can get your token.
const [auth_token, setAuthToken ] = useState(null);
const getData = async () => {
try {
const value = await AsyncStorage.getItem('auth_token')
if(value !== null) {
setAuthToken(value)
}
} catch(e) {
// error reading value
}
}
Here you can use it like this.
axios
.post('http://10.0.2.2:8000/api/register', Data56)
.then(function (response) {
storeData(réponse.token)
})
.catch(error => {
alert(JSON.stringify(error.response, 'catch'));
});
}
After you set your token , on the start of the application get the token and store it the redux-store. of the token is null redirect user to login else redirect to Home Screen .
on API call use can use that token like this.
var config = {
method: 'post',
url: 'https://my.api.url.com//v1/delete_user_account.php?id=98744',
headers: {
'Authorization': `Bearer ${auth_token}`,
'Content-Type': 'application/json'
},
data : data
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});
Here is the code where you can store a token and get that token back.
import AsyncStorage from '#react-native-async-storage/async-storage';
import React from 'react';
import {Text, TouchableOpacity, View} from 'react-native';
import {SafeAreaProvider} from 'react-native-safe-area-context';
const App = () => {
const auth_session_token = 'AwdcE39dsC#43d#2023jlkre2DWjKLD';
const store_token = (async = value => {
AsyncStorage.setItem('token', `${value}`)
.then(() => {
alert('token is stored');
})
.catch(err => {
alert('some Error', JSON.stringify(err));
});
});
const get_auth_token = () => {
AsyncStorage.getItem('token')
.then(token => {
console.log(token);
alert(JSON.stringify(token));
})
.catch(err => {
console.log('Some Eorr');
alert('THere is some error', JSON.stringify(err));
});
};
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<TouchableOpacity
onPress={() => store_token(auth_session_token)}
style={{
width: 200,
height: 50,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#0090FF',
}}>
<Text style={{color: '#fff', fontWeight: 'bold'}}>Store Token</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => get_auth_token()}
style={{
width: 200,
height: 50,
marginTop: 60,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#0090FF',
}}>
<Text style={{color: '#fff', fontWeight: 'bold'}}>Get Token</Text>
</TouchableOpacity>
</View>
);
};
export default App;
While handling tokens it's better to use react-native-keychain. It is more secure than async storage and better for storing sensitive data.
install and import keychain :
npm i react-native-keychain --legacy-peer-deps
import * as Keychain from 'react-native-keychain';
In your case try this approach :
axios({
method: 'POST',
url: 'http://127.0.0.1:8000/api/register',
data: Data56,
});
.then(async function (response) {
storeData(response.data.token);
await Keychain.setGenericPassword(response.data.token);
alert('sucess');
.catch(error => {
alert(JSON.stringify(error.response, 'catch'));
});
}
If your tokens are not strings and you need to store an Object, just stringify them using
JSON.stringify(response.data.token)
and retrieve the token anywhere using :
const token = await Keychain.getGenericPassword();
You can also reset a session easily using :
await Keychain.resetGenericPassword();
Here is simple way using the Async Storage it's similar window.localStorage.
Install React Native Async Storage
npm install #react-native-async-storage/async-storage --save
Now make a api helper name with api.js
import AsyncStorage from '#react-native-async-storage/async-storage'
import axios from 'axios';
export let BASE_URL = 'http://127.0.0.1:8000/api/'
export let GET = async (params)=>{
let token = await AsyncStorage.getItem('#token')
return axios({method:'GET', url:BASE_URL+params,
headers: {
Authorization: token, //here use Bearer token with prefix
}
})
}
export let POST = async (endpoint,params)=>{
let token = await AsyncStorage.getItem('#token')
return axios({
method:'POST',
url:BASE_URL+endpoint,
headers: {
Authorization: token, //here use Bearer token with prefix
},
data:JSON.stringify(params)
})
}
Now store the token into the Async Storage
import {POST} from "./helper/api"
import AsyncStorage from '#react-native-async-storage/async-storage'
POST('register',Data56).then(function (response) {
AsyncStorage.setItem('#token',response.data.token)
alert('sucess');
}).catch(error => {
alert(JSON.stringify(error.response, 'catch'));
});
}
After the storing a token in async storage next api will call with auth token

How to solve React Native expo authSession google login issue?

I wanted to implement login with google feature on my React native app. I'm using expo-cli and I used expo authSession for this.
LoginScreen.js
import * as React from "react";
import * as WebBrowser from "expo-web-browser";
import * as Google from "expo-auth-session/providers/google";
import { Button, View, Text, TouchableOpacity } from "react-native";
WebBrowser.maybeCompleteAuthSession();
export default function LoginScreen() {
const [googleAccessToken, setGoogleAccessToken] = React.useState(null);
const [userInfo, setUserInfo] = React.useState(false);
const [request, response, promptAsync] = Google.useAuthRequest({
expoClientId: "###",
iosClientId: "###",
androidClientId: "###",
webClientId: "###",
});
React.useEffect(() => {
if (response?.type === "success") {
const { authentication } = response;
setGoogleAccessToken(authentication.accessToken);
fetchUserInfo();
}
}, [response]);
const fetchUserInfo = () => {
if (googleAccessToken) {
fetch("https://www.googleapis.com/oauth2/v3/userinfo", {
headers: { Authorization: `Bearer ${googleAccessToken}` },
})
.then((response) => response.json())
.then((userInfoObj) => {
setUserInfo(userInfoObj);
console.log(userInfoObj);
})
.catch((err) => {
console.log(err);
});
}
};
return (
<View>
<Button
disabled={!request}
title="Login Here"
onPress={() => {
promptAsync();
}}
/>
{userInfo ? <Text>{userInfo.email}</Text> : <Text>Nope</Text>}
</View>
);
}
But once, I click LOGIN HERE button and login with google account it doesn't set userInfo state in the first time. I have to click LOGIN HERE button again to login. Even though I have authenticated with google, it doesn't set userInfo state in the first time. But once I click LOGIN HERE again and after continue the process, then it works. How can I solve this issue?
Also I'm getting this warning as well.
Warning
EventEmitter.removeListener('url', ...): Method has been deprecated. Please instead use `remove()` on the subscription returned by `EventEmitter.addListener`.
at node_modules/react-native/Libraries/vendor/emitter/_EventEmitter.js:164:4 in EventEmitter#removeListener
at node_modules/react-native/Libraries/EventEmitter/NativeEventEmitter.js:108:4 in removeListener
at node_modules/react-native/Libraries/Linking/Linking.js:57:4 in removeEventListener
at node_modules/expo-web-browser/build/WebBrowser.js:354:4 in _stopWaitingForRedirect
at node_modules/expo-web-browser/build/WebBrowser.js:347:31 in _openAuthSessionPolyfillAsync
I found the answer for this question by myself. You need to put fetchUserInfo() inside useEffect.
import * as React from "react";
import * as WebBrowser from "expo-web-browser";
import * as Google from "expo-auth-session/providers/google";
import { Button, View, Text, TouchableOpacity } from "react-native";
WebBrowser.maybeCompleteAuthSession();
export default function LoginScreen() {
const [googleAccessToken, setGoogleAccessToken] = React.useState(null);
const [userInfo, setUserInfo] = React.useState(false);
const [request, response, promptAsync] = Google.useAuthRequest({
expoClientId: "###",
iosClientId: "###",
androidClientId: "###",
webClientId: "###",
});
React.useEffect(() => {
const fetchUserInfo = () => {
if (googleAccessToken) {
fetch("https://www.googleapis.com/oauth2/v3/userinfo", {
headers: { Authorization: `Bearer ${googleAccessToken}` },
})
.then(response => response.json())
.then(userInfoObj => {
setUserInfo(userInfoObj);
console.log(userInfoObj);
})
.catch(err => {
console.log(err);
});
}
};
if (response?.type === "success") {
const { authentication } = response;
setGoogleAccessToken(authentication.accessToken);
fetchUserInfo();
}
}, [response]);
return (
<View>
<Button
disabled={!request}
title="Login Here"
onPress={() => {
promptAsync();
}}
/>
{userInfo ? <Text>{userInfo.email}</Text> : <Text>Nope</Text>}
</View>
);
}

connecting stripe to react native app error

I'm trying test a payment using stripe for my react native project. But when I press the pay button i get an error stating. since there is not an official doc for stripe for react native, its been challenging
here the error
TypeError: null is not an object (evaluating 'StripeBridge.createPayment')
onPaymentPress
PaymentScreen.js:17:33
what that error mean exactly and how do I remove it?
essentially my goal is for the user to make a one time payment once they enter their details and press the pay button
here is my code
import React, { useState } from 'react'
import { Image, Text, TextInput, TouchableOpacity, View,ImageBackground,NativeModules} from 'react-native'
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import styles from './styles';
var StripeBridge = NativeModules.StripeBridge;
export default function PaymentScreen({navigation}) {
const [ccNumber, setCCNumber] = useState('')
const [month, setMonth] = useState('')
const [year, setYear] = useState('')
const [zipCode, setZipCode] = useState('')
const [cvv, setCvv] = useState('')
const onPaymentPress = () => {
fetch('http://localhost:3001/createStripePaymentIntent', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
})
.then(response => response.json())
.then(responseJson => {
console.log(responseJson.setupIntentId);
StripeBridge.createPayment(
responseJson.setupIntentId,
ccNumber,
month,
year,
cvv,
zipCode,
(error, res, payment_method) => {
if (res == 'SUCCESS') {
Alert.alert('Stripe Payment', 'Your Stripe payment succeeded', [
{text: 'OK', onPress: () => console.log('OK Pressed')},
]);
}
},
);
})
.catch(error => {
console.error(error);
});
// setTimeout(navigation.navigate("Products"), 1000)
}
return (
</View>
......
style={styles.button}
onPress={() => onPaymentPress()}>
<Text style={styles.buttonTitle}>Submit Payment</Text>
</TouchableOpacity>
<View style={styles.footerView}>
<Text style={styles.footerText}> Payment secured by Stripe <Text style={styles.footerLink}></Text></Text>
</View>
</KeyboardAwareScrollView>
</View>
</ImageBackground>
)
}

React Native Navigate To Another Screen Automatically

I am trying to navigate back to the LogIn screen as soon as logout.js is hit.
At one point this seemed to work and then stopped and I can't work out why.
Here is my code:
import React, { Component } from 'react';
import { Text, View, Button } from 'react-native';
import * as SecureStore from 'expo-secure-store';
import { globalStyles } from '../styles/global';
export default class App extends Component {
componentDidMount() {
(async () => {
const token = await SecureStore.getItemAsync('token');
// Your fetch code
this.setState({loaded:false, error: null});
let url = 'https://www.example.com/api/auth/logout';
let h = new Headers();
h.append('Authorization', `Bearer ${token}`);
h.append('Content-Type', 'application/json');
h.append('X-Requested-With', 'XMLHttpRequest');
let req = new Request(url, {
headers: h,
method: 'GET'
});
fetch(req)
.then(response=>response.json())
//.then(this.showData)
.catch(this.badStuff)
})();
this.deleteToken()
}
badStuff = (err) => {
this.setState({loaded: true, error: err.message});
}
deleteToken() {
(async () => {
const token = await SecureStore.deleteItemAsync('token')
});
this.goToLogInScreen()
}
goToLogInScreen() {
this.props.navigation.navigate('LogIn')
}
render() {
return (
<View style={globalStyles.container}>
<Button
title="Go to Details"
onPress={() => this.props.navigation.navigate('LogIn')}
/>
</View>
);
}
}
I need the code to:
Send the command to the API to log out
Delete the token from SecureStore
Navigate to the LogIn screen (containing the log in form).

Infinite FlatList problem - React Native , Expo

I am using Expo for developing react-native applications.
I want to make an Infinite list, but every time onEndReached event is fired, FlatList is refreshed automatically scrolls to the top of the page!
import React, { useState, useEffect } from "react";
import { SafeAreaView, Text, FlatList } from "react-native";
export default function App() {
const [config, setConfig] = useState({
result: [],
page: 0
});
async function fetchData() {
const response = await fetch("http://192.168.2.49:3500/q", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({ page: config.page })
});
const data = await response.json();
setConfig({
result: [...config.result, ...data],
page: config.page++
});
}
const onEndReached = async () => {
await setConfig({
page: config.page++
});
fetchData();
};
useEffect(() => {
fetchData();
}, []);
return (
<SafeAreaView>
<Text>Current Page : {config.page}</Text>
<FlatList
data={config.result}
renderItem={o => <Text>X :{o.item.t.c}</Text>}
keyExtractor={item => item._id}
onEndReached={() => onEndReached()}
onEndReachedThreshold={0}
></FlatList>
</SafeAreaView>
);
}
You are calling setConfig twice, before calling fetchData and after a successful API request. Which triggers a rerender.
I refactored your code, try this.
import React, { useState, useEffect, useCallback } from 'react';
import {
SafeAreaView,
Text,
FlatList,
NativeSyntheticEvent,
NativeScrollEvent,
} from 'react-native';
export default function App() {
const [config, setConfig] = useState({
result: [],
page: 0,
});
const [isScrolled, setIsScrolled] = useState(false);
const fetchData = useCallback(() => {
async function runFetch() {
const response = await fetch('http://192.168.2.49:3500/q', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ page: config.page }),
});
const data = await response.json();
setConfig({
result: [...config.result, ...data],
page: config.page++,
});
}
runFetch();
}, [config.page, config.result]);
const onEndReached = useCallback(() => fetchData(), [fetchData]);
const onScroll = useCallback(
e => setIsScrolled(e.nativeEvent.contentOffset.y > 0),
[],
);
useEffect(() => {
fetchData();
}, [fetchData]);
return (
<SafeAreaView>
<Text>Current Page : {config.page}</Text>
<FlatList
data={config.result}
keyExtractor={item => item._id}
onEndReached={onEndReached}
onEndReachedThreshold={0.1}
onScroll={onScroll}
renderItem={o => <Text>X :{o.item.t.c}</Text>}
/>
</SafeAreaView>
);
}