Related
We are trying to use Expo authentication with Okta as stated here:
https://docs.expo.dev/guides/authentication/#okta
Expo has very good documentation for lot's of stuff, but for the Okta authentication unfortunately we could not sort out how to use the library in a correct way.
Currently, with lot's of suffering (mostly because of the ambiguity in Okta's configuration pages), we came to a certain point where the following code correctly responds the code parameter. This is the exact same part from Expo documentation:
React.useEffect(() => {
if (response?.type === 'success') {
const { code } = response.params;
}
}, [response]);
But unfortunately we could not find any method how we can use the parameter code to get the scope information, email, name, etc...
Can anybody guide us how we can use the object code to retrieve these data? (The Okta documentation is not clear for this either, so we are stuck.)
Edit 1:
The response has the following structure:
response: {
"type": "success",
"error": null,
"url": "http://localhost:19006/?code=fUMjE4kBX2QZXXXXXX_XXXXXXXMQ084kEPrTqDa9FTs&state=3XXXXXXXXz",
"params": {
"code": "fUMjE4kBX2QZXXXXXX_XXXXXXXMQ084kEPrTqDa9FTs",
"state": "3XXXXXXXXz"
},
"authentication": null,
"errorCode": null
}
Edit 2:
Calling exchangeCodeAsync also yields errors.
Code:
const tokenRequestParams = {
code: code,
clientId: config.okta.clientId,
redirectUri: oktaRedirectUri,
extraParams: {
code_verifier: authRequest.codeVerifier
},
}
const tokenResult = await exchangeCodeAsync(tokenRequestParams, discovery);
Error:
TokenRequest.ts:205 Uncaught (in promise) Error: Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The authorization server MAY return an HTTP 401 (Unauthorized) status code to indicate which HTTP authentication schemes are supported. If the client attempted to authenticate via the "Authorization" request header field, the authorization server MUST respond with an HTTP 401 (Unauthorized) status code and include the "WWW-Authenticate" response header field matching the authentication scheme used by the client.
More info: Client authentication failed. Either the client or the client credentials are invalid.
at AccessTokenRequest.<anonymous> (TokenRequest.ts:205:1)
at Generator.next (<anonymous>)
at asyncGeneratorStep (asyncToGenerator.js:3:1)
at _next (asyncToGenerator.js:22:1)
PS: I asked the same question to the Expo forums also here. If we can solve there, I plan to reflect to here also for wider audience. (The method can be related with Okta rather than Expo itself.)
there are two ways to use Okta in React Native
1. By Restful APIs, using fetch/Axios
2. By Using Native SDK
By Restful APIs, using fetch/Axios
here is the full code of okta using restful
import React, { useState } from "react";
import {
ScrollView,
StyleSheet,
Text,
View,
TouchableOpacity,
Platform,
} from "react-native";
import {
useAutoDiscovery,
useAuthRequest,
makeRedirectUri,
exchangeCodeAsync,
} from "expo-auth-session";
import { maybeCompleteAuthSession } from "expo-web-browser";
import axios from "axios";
const oktaConfig = {
okta_issuer_url: "",
okta_client_id: "",
okta_callback_url: "com.okta.<OKTA_DOMAIN>:/callback",
};
export default App = (props) => {
const useProxy = true;
if (Platform.OS === "web") {
maybeCompleteAuthSession();
}
const discovery = useAutoDiscovery(oktaConfig.okta_issuer_url);
// When promptAsync is invoked we will get back an Auth Code
// This code can be exchanged for an Access/ID token as well as
// User Info by making calls to the respective endpoints
const [authRequest, response, promptAsync] = useAuthRequest(
{
clientId: oktaConfig.okta_client_id,
scopes: ["openid", "profile"],
redirectUri: makeRedirectUri({
native: oktaConfig.okta_callback_url,
useProxy,
}),
},
discovery
);
async function oktaCognitoLogin() {
const loginResult = await promptAsync({ useProxy });
ExchangeForToken(loginResult, authRequest, discovery);
}
return (
<View style={styles.container}>
<View style={styles.buttonContainer}>
<TouchableOpacity
style={styles.equalSizeButtons}
onPress={() => oktaCognitoLogin()}
>
<Text style={styles.buttonText}>Okta Login</Text>
</TouchableOpacity>
</View>
<ScrollView>
{response && <Text>{JSON.stringify(response, null, 2)}</Text>}
</ScrollView>
</View>
);
};
this is how, we can get exchange token and then get user info by using restful api
//After getting the Auth Code we need to exchange it for credentials
async function ExchangeForToken(response, authRequest, discovery) {
// React hooks must be used within functions
const useProxy = true;
const expoRedirectURI = makeRedirectUri({
native: oktaConfig.okta_callback_url,
useProxy,
})
const tokenRequestParams = {
code: response.params.code,
clientId: oktaConfig.okta_client_id,
redirectUri: expoRedirectURI,
extraParams: {
code_verifier: authRequest.codeVerifier
},
}
const tokenResult = await exchangeCodeAsync(
tokenRequestParams,
discovery
)
const creds = ExchangeForUser(tokenResult)
const finalAuthResult = {
token_res : tokenResult,
user_creds : creds
}
console.log("Final Result: ", finalAuthResult)
}
this is how we can get user info by using restful api
async function ExchangeForUser(tokenResult) {
const accessToken = tokenResult.accessToken;
const idToken = tokenResult.idToken;
//make an HTTP direct call to the Okta User Info endpoint of our domain
const usersRequest = `${oktaConfig.okta_issuer_url}/v1/userinfo`
const userPromise = await axios.get(usersRequest, {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
console.log(userPromise, "user Info");
}
const styles = StyleSheet.create({
container: {
margin: 10,
marginTop: 20,
},
buttonContainer: {
flexDirection: "row",
alignItems: "center",
margin: 5,
},
equalSizeButtons: {
width: "50%",
backgroundColor: "#023788",
borderColor: "#6df1d8",
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
padding: 9,
borderWidth: 1,
shadowColor: "#6df1d8",
shadowOpacity: 8,
shadowRadius: 3,
shadowOffset: {
height: 0,
width: 0,
},
},
buttonText: {
color: "#ffffff",
fontSize: 16,
},
});
Reference Code
By Using Native SDK
for native SDK, you can use okta-react-native package like this
Login Screen
import React from 'react';
import {
Alert,
Button,
StyleSheet,
TextInput,
View,
ActivityIndicator
} from 'react-native';
import {
signIn,
introspectIdToken
} from '#okta/okta-react-native';
export default class CustomLogin extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: false,
username: '',
password: '',
};
}
async componentDidMount() {
}
signInCustom = () => {
this.setState({ isLoading: true });
signIn({ username: this.state.username, password: this.state.password })
.then(() => {
introspectIdToken()
.then(idToken => {
this.props.navigation.navigate('ProfilePage', { idToken: idToken, isBrowserScenario: false });
}).finally(() => {
this.setState({
isLoading: false,
username: '',
password: '',
});
});
})
.catch(error => {
// For some reason the app crashes when only one button exist (only with loaded bundle, debug is OK) 🤦♂️
Alert.alert(
"Error",
error.message,
[
{
text: "Cancel",
onPress: () => console.log("Cancel Pressed"),
style: "cancel"
},
{ text: "OK", onPress: () => console.log("OK Pressed") }
]
);
this.setState({
isLoading: false
});
});
}
render() {
if (this.state.isLoading) {
return (
<View style={styles.container}>
<ActivityIndicator size="large" />
</View>
);
}
return (
<View style={styles.container}>
<TextInput
style={styles.input}
placeholder='Username'
onChangeText={input => this.setState({ username: input })}
testID="username_input"
/>
<TextInput
style={styles.input}
placeholder='Password'
onChangeText={input => this.setState({ password: input })}
testID="password_input"
/>
<Button
onPress={this.signInCustom}
title="Sign in"
testID='sign_in_button'
/>
<View style={styles.flexible}></View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
input: {
height: 40,
width: '80%',
margin: 12,
borderWidth: 1,
padding: 10,
},
flexible: {
flex: 1,
}
});
Profile Screen
import React from 'react';
import {
Text,
Button,
StyleSheet,
TextInput,
View,
} from 'react-native';
import {
signOut,
revokeAccessToken,
revokeIdToken,
clearTokens,
} from '#okta/okta-react-native';
export default class ProfilePage extends React.Component {
constructor(props) {
super(props);
this.state = {
idToken: props.route.params.idToken,
isBrowserScenario: props.route.params.isBrowserScenario
};
}
logout = () => {
if (this.state.isBrowserScenario == true) {
signOut().then(() => {
this.props.navigation.popToTop();
}).catch(error => {
console.log(error);
});
}
Promise.all([revokeAccessToken(), revokeIdToken(), clearTokens()])
.then(() => {
this.props.navigation.popToTop();
}).catch(error => {
console.log(error);
});
}
render() {
return (
<View style={styles.container}>
<Text testID="welcome_text">Welcome back, {this.state.idToken.preferred_username}!</Text>
<Button
onPress={this.logout}
title="Logout"
testID="logout_button"
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Reference Code
Please, am new to react native. Am trying to get the current location through google places autocomplete for android in expo but an getting the error 'TypeError: null is not an object (evaluating 'RNFusedLocation.getCurrentPosition')'
below is my code
Please, am new to react native. Am trying to get the current location through google places autocomplete for android in expo but an getting the error 'TypeError: null is not an object (evaluating 'RNFusedLocation.getCurrentPosition')'
below is my code
App.js
import { StyleSheet, Text, View, StatusBar, PermissionsAndroid, Platform,} from 'react-native';
import HomeScreen from './src/screens/HomeScreen/HomeScreen';
import DestinationSearch from './src/screens/DestinationSearch/DestinationSearch';
import SearchResults from './src/screens/SearchResults/SearchResults';
import { useEffect, useState } from 'react';
//import * as Location from 'expo-location';
import Geolocation, { getCurrentPosition } from 'react-native-geolocation-service';
navigator.geolocation = require('react-native-geolocation-service');
export default function App() {
const androidPermission = async () => {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
title: "Uber App location Permission",
message:
"Uber App needs access to your location " +
"so you can take awesome rides.",
buttonNeutral: "Ask Me Later",
buttonNegative: "Cancel",
buttonPositive: "OK"
}
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log("You can use the location");
} else {
console.log("Location permission denied");
}
} catch (err) {
console.warn(err);
}
};
useEffect(() => {
if (androidPermission) {
Geolocation.getCurrentPosition(
(position) => {
console.log(position);
},
(error) => {
// See error code charts below.
console.log(error.code, error.message);
},
{ enableHighAccuracy: true, timeout: 15000, maximumAge: 10000 }
);
}
}, [])
useEffect(() => {
if(Platform.OS == 'android') {
androidPermission()
} else{
//IOS
Geolocation.requestAuthorization();
}
}, [])
return (
<View>
{/* <HomeScreen /> */}
<DestinationSearch />
{/* <SearchResults /> */}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
marginTop:StatusBar.currentHeight
},
});
`
DestinationSearch.jsx
import { View, Text, StyleSheet, SafeAreaView, StatusBar } from 'react-native'
import React, {useEffect, useState,} from 'react'
import { GooglePlacesAutocomplete } from 'react-native-google-places-autocomplete';
import {GOOGLE_MAPS_APIKEY} from '#env'
import PlaceRow from './PlaceRows';
const DestinationSearch = () => {
const [originPlace,setOriginPlace] = useState(null)
const [destinationPlace, setDestinationPlace] = useState(null)
useEffect(() => {
console.log('useEffect is called')
if (originPlace && destinationPlace) {
console.warn('Redirect to results')
}
}, [originPlace, destinationPlace])
return (
<SafeAreaView>
<View style={styles.container}>
<GooglePlacesAutocomplete
nearbyPlacesApi = 'GooglePlacesSearch'
placeholder = 'From...'
listViewDisplayed = 'auto'
debounce = {400}
currentLocation = {true}
currentLocationLabel='Current location'
minLenght = {2}
enabledPoweredByContainer = {true}
fetchDetails = {true}
autoFoccus = {true}
renderRow={(data)=> <PlaceRow data={data}/>}
query ={{
key: GOOGLE_MAPS_APIKEY ,
language :'en'
}}
styles={{
container: styles.autocompleteContainer,
textInput: styles.textInput,
listView: styles.listView,
seperator: styles.separator
}}
onPress = {(data, details = null)=> {
setOriginPlace({data, details})
console.log(currentLocation)
}}
/>
<GooglePlacesAutocomplete
nearbyPlacesApi = 'GooglePlacesSearch'
placeholder = 'To...'
listViewDisplayed = 'auto'
debounce = {400}
minLenght = {2}
enabledPoweredByContainer = {true}
fetchDetails = {true}
autoFoccus = {true}
query ={{
key: GOOGLE_MAPS_APIKEY ,
language :'en'
}}
renderRow={(data)=> <PlaceRow data={data}/>}
styles={{
container: {
...styles.autocompleteContainer,
top: 70
},
textInput: styles.textInput,
seperator: styles.separator
}}
onPress = {(data, details = null)=> {
setDestinationPlace({data, details})
}}
/>
I coded an app that will create a modal that gives the option to alert reception that a customer is coming. However, when running the app on two phones, all changes are local. Why is that? I want to be able to "Alert Reception" and have reception receive a notification of the customer and be able to click More Info and see all the details about it.
We scan a customer QR code, and allow the security guard to validate and then alert reception and send the info to them.
Any ideas? We are using AWS and React Native. I would like to alert everyone and then be able to have the reception see the customer is coming and get the info.
import { StyleSheet, Text, View, Image, Button, Modal, Platform, ScrollView } from 'react-native';
import { BarCodeScanner } from 'expo-barcode-scanner';
import Constants from 'expo-constants';
import * as Notifications from 'expo-notifications';
import { Audio, Video } from 'expo-av';
//For push notifications
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: false,
}),
});
export default function App() {
//QR Scanner
const [hasPermission, setHasPermission] = useState(null);
const [scanned, setScanned] = useState(false);
const [newData, setNewData] = useState('');
//Modal Views
const [homeScreenVisible, setHomeScreenVisible] = useState(true);
const [customerModalVisible, setCustomerModalVisible] = useState(false);
const [customerModalDetailedVisible, setCustomerModalDetailedVisible] = useState(false);
const [qrScannerVisible, setQRScannerVisible] = useState(false);
//Push notifications
const [expoPushToken, setExpoPushToken] = useState('');
const [notification, setNotification] = useState(false);
const notificationListener = useRef();
const responseListener = useRef();
//useEffects are rendered upon app start. We use the [] to make sure they are only rendered once. If you want them to update, add in the array / state var and upon update the useEffect will run again.
//For Bar Code Scanner
useEffect(() => {
(async () => {
const { status } = await BarCodeScanner.requestPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);
//For Push Notifications
useEffect(() => {
registerForPushNotificationsAsync().then(token => setExpoPushToken(token));
// This listener is fired whenever a notification is received while the app is foregrounded
notificationListener.current = Notifications.addNotificationReceivedListener(notification => {
setNotification(notification);
});
// This listener is fired whenever a user taps on or interacts with a notification (works when app is foregrounded, backgrounded, or killed)
responseListener.current = Notifications.addNotificationResponseReceivedListener(response => {
console.log(response);
console.log('Someone clicked on the push notification')
});
return () => {
Notifications.removeNotificationSubscription(notificationListener.current);
Notifications.removeNotificationSubscription(responseListener.current);
};
}, []);
//Bar Code scanner function, reads the data (which is a aws s3 endpoint served through cloudfront), then I run a fetch on the url (aka data), and then turn the string response into a json. Then I set newData equal to the JSON object via React Hooks. Then I turn the QR scanner visible off, and then open the customer modal.
const handleBarCodeScanned = ({ type, data }) => {
setScanned(true);
console.log(data) // Should be the url JSON endpoint
fetch(data)
.then(response => response.json())
.then(response => {
console.log(response)
setNewData(response)
});
setQRScannerVisible(false);
setCustomerModalVisible(true);
// const playbackObject = new Audio.Sound();
};
if (hasPermission === null) {
return <Text>Requesting for camera permission</Text>;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
// We are returning a View with multiple JS {} fragments that turn off and on depending on state. There is an issue with setScanned(false) that sometimes the scanner is not set to false and it will not rescan. If you run into that problem, try adding setScanned(false).
return (
<ScrollView style={{flex: 1, backgroundColor: 'black'}}>
<View id="view" style={styles.container}>
{homeScreenVisible &&
<>
<Image
source={{
uri: "https://d1s68zh8fdz4eb.cloudfront.net/logo.png"
}}
style={{
height: 500,
width: '100%',
// borderWidth: 5,
// borderColor: '#fff',
marginTop: 30,
marginBottom: 20
}}
/>
<Button title="Scan QR Code" style={styles.moreInfo} onPress={() => {
setScanned(false)
setHomeScreenVisible(false)
setQRScannerVisible(true)
}} />
</>
}
{qrScannerVisible &&
<>
<Image
source={{
uri: "https://d1s68zh8fdz4eb.cloudfront.net/logo.png"
}}
style={{
height: 100,
width: 100,
borderWidth: 5,
marginBottom: 60
}}
/>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
// style={StyleSheet.absoluteFillObject}
style={{width: 300, height: 300, borderWidth: 5, borderColor: '#fff'}}
//If you want to switch to a absolute fill object, add the target area on the screen for scanning and try to add a sound.
// If you want to use the front facing or rear facing, include type={'front'} or put 'back'
/>
<Text style={{color: '#fff', fontSize: 30, marginTop: 30, marginBottom: 30}}>Scan QR Code</Text>
<Button title="Home" onPress={() => {
setHomeScreenVisible(true)
setQRScannerVisible(false)
setScanned(false)
}} />
</>
}
{/* Used for re scanning things. It is commented out because someoen can always go back to home and rescan. However, if you want to reintroduce a rescan button, you can do it with the below code.*/}
{/* {scanned && <Button title={'Tap to Scan Again'} onPress={() => {
setScanned(false)
}} />}
*/}
{customerModalVisible &&
<Modal style={styles.modal}>
<View style={styles.modalView}>
<Image
source={{
uri: "https://d1s68zh8fdz4eb.cloudfront.net/logo.png"
}}
style={{
height: 100,
width: 100,
borderWidth: 5,
marginBottom: 60
}}
/>
<View style={styles.modalInside}>
<Image
style={{
height: 300,
width: 300,
marginBottom: 20,
borderWidth: 5,
borderColor: 'gold'
}}
source={{
uri: newData.photo,
}}
/>
<Text style={styles.modalTextName}>{newData.name}</Text>
<Text style={styles.modalTextSecondary}>Favorite Drink: {newData.favoriteDrink}</Text>
<Text style={styles.modalTextSecondary}>Lead Contact: Leah</Text>
</View>
<Button title="Alert Reception" style={styles.closeButton} onPress={async () => {
await sendPushNotification(expoPushToken);
alert('Reception has been notified.')
}} />
<Button title="More Info" style={styles.moreInfo} onPress={() => {
// PushCustomerStatus();
setCustomerModalDetailedVisible(true)
setCustomerModalVisible(false)
setScanned(false)
setQRScannerVisible(false)
// Add modal # 2 or page navigation here
}} />
<Button title="Close" style={styles.closeButton} onPress={() => {
setHomeScreenVisible(true)
setCustomerModalVisible(false)
setScanned(false)
setQRScannerVisible(false)
}} />
</View>
</Modal>}
{customerModalDetailedVisible &&
<>
<Image
source={{
uri: "https://d1s68zh8fdz4eb.cloudfront.net/logo.png"
}}
style={{
height: 100,
width: 100,
// borderWidth: 5,
// borderColor: '#fff'
}}
/>
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'space-around',
width: '95%',
borderWidth: 5,
borderColor: 'gold',
}}>
{/* <Text>Your expo push token: {expoPushToken}</Text> */}
<View style={{ alignItems: 'flex-start', justifyContent: 'center', backgroundColor: '#fff', flex: 1, width: '100%', padding: 15}}>
<Image
style={{
height: 300,
width: 300,
marginBottom: 20,
// borderWidth: 5,
// borderColor: 'gold'
}}
source={{
uri: newData.photo,
}}
/>
<Text>Customer: {newData.name}</Text>
<Text>Arm Length: {newData.armLength}</Text>
<Text>Address: {newData.address}</Text>
<Text>Phone: {newData.phone}</Text>
<Text>Email: {newData.email}</Text>
<Text>Favorite Drink: {newData.favoriteDrink}</Text>
<Text>Height: {newData.height}</Text>
<Text>Inseam: {newData.inseam}</Text>
<Text>Neck Length: {newData.neckLength}</Text>
<Text>Customer: {newData.name}</Text>
<Text>Arm Length: {newData.armLength}</Text>
<Text>Address: {newData.address}</Text>
<Text>Phone: {newData.phone}</Text>
<Text>Email: {newData.email}</Text>
<Text>Favorite Drink: {newData.favoriteDrink}</Text>
<Text>Height: {newData.height}</Text>
<Text>Inseam: {newData.inseam}</Text>
<Text>Neck Length: {newData.neckLength}</Text>
<Text>Customer: {newData.name}</Text>
<Text>Arm Length: {newData.armLength}</Text>
<Text>Address: {newData.address}</Text>
<Text>Phone: {newData.phone}</Text>
<Text>Email: {newData.email}</Text>
<Text>Favorite Drink: {newData.favoriteDrink}</Text>
<Text>Height: {newData.height}</Text>
<Text>Inseam: {newData.inseam}</Text>
<Text>Neck Length: {newData.neckLength}</Text>
<Text>Customer: {newData.name}</Text>
<Text>Arm Length: {newData.armLength}</Text>
<Text>Address: {newData.address}</Text>
<Text>Phone: {newData.phone}</Text>
<Text>Email: {newData.email}</Text>
<Text>Favorite Drink: {newData.favoriteDrink}</Text>
<Text>Height: {newData.height}</Text>
<Text>Inseam: {newData.inseam}</Text>
<Text>Neck Length: {newData.neckLength}</Text>
{/* I can return it as an object above and use dot notation or do something similar below but use a string */}
{/* <Text>Data: {notification && JSON.stringify(notification.request.content.data)}</Text> */}
<Button
title="Press to Send Notification"
onPress={async () => {
await sendPushNotification(expoPushToken);
}}
/>
<Button
title="Home"
onPress={() => {
setHomeScreenVisible(true)
setScanned(false)
setCustomerModalDetailedVisible(false)
setQRScannerVisible(false)
}}
/>
</View>
</View>
</>
}
</View>
</ScrollView>
);
}
// Can use this function below, OR use Expo's Push Notification Tool-> https://expo.io/notifications
async function sendPushNotification(expoPushToken) {
const message = {
to: expoPushToken,
sound: 'default',
title: 'Customer Arriving!',
body: 'Please greet them at the door.',
};
await fetch('https://exp.host/--/api/v2/push/send', {
method: 'POST',
headers: {
Accept: 'application/json',
'Accept-encoding': 'gzip, deflate',
'Content-Type': 'application/json',
},
body: JSON.stringify(message),
});
}
async function registerForPushNotificationsAsync() {
let token;
if (Constants.isDevice) {
const { status: existingStatus } = await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== 'granted') {
alert('Failed to get push token for push notification!');
return;
}
token = (await Notifications.getExpoPushTokenAsync()).data;
console.log(token);
} else {
alert('Must use physical device for Push Notifications');
}
if (Platform.OS === 'android') {
Notifications.setNotificationChannelAsync('default', {
name: 'default',
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: '#FF231F7C',
});
}
return token;
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'center',
backgroundColor: 'black',
paddingTop: 60,
},
modal: {
// marginTop: 100,
},
modalView: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'black'
},
modalInside: {
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 20
},
modalTextName: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 5,
color: 'white'
},
modalTextSecondary: {
color: '#fff',
marginBottom: 5,
},
closeButton: {
marginTop: 100,
},
moreInfo: {
marginTop: 100,
}
});
By live alerts, I assume you mean text notifications. One way to push out text notifications using AWS is using the Simple Notification Service. Because you are using a React app, use the AWS SDK for JavaScript. Using the SDK, you can create app logic to fire off text notifications in response to certain events.
You can find examples here:
https://github.com/awsdocs/aws-doc-sdk-examples/tree/master/javascriptv3/example_code/sns/src
I was trying to develop a login page with the Django rest framework as the backend. The backend is working perfectly whereas I can't even set up react js. I am getting an error in the Index.js file of react. It tells "Invalid hook call. Hooks can only be called inside of the body of a function component"
This is what the error I get
App.js
import React from 'react';
import './App.css';
import Paperbase from './Layout/Paperbase'
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Login from './Layout/Login/Login'
import Register from './Layout/Register/Register'
export function App() {
return (
<BrowserRouter>
<Switch>
<Route path="/dashboard" render={() => <Paperbase /> } />
<Route path="/account/login" render={() =><Login />} />
<Route path="/account/register" render={() => <Register />} />
</Switch>
</BrowserRouter>
)
}
export default App
Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import store from './store';
import { Provider } from 'react-redux';
import { render } from 'react-dom';
ReactDOM.render(
(<Provider store={store}>
<App/>
</Provider>),
document.getElementById('root') || document.createElement('div') // for testing purposes
);
serviceWorker.unregister();
Login.js
import React from 'react';
import Avatar from '#material-ui/core/Avatar';
import Button from '#material-ui/core/Button';
import CssBaseline from '#material-ui/core/CssBaseline';
import TextField from '#material-ui/core/TextField';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import Checkbox from '#material-ui/core/Checkbox';
import Grid from '#material-ui/core/Grid';
import LockOutlinedIcon from '#material-ui/icons/LockOutlined';
import Typography from '#material-ui/core/Typography';
import Container from '#material-ui/core/Container';
import { withStyles } from '#material-ui/core/styles';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { login } from '../../actions/auth';
const styles = theme => ({
'#global': {
body: {
backgroundColor: theme.palette.common.white,
},
},
paper: {
marginTop: theme.spacing(25),
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
},
avatar: {
margin: theme.spacing(1),
backgroundColor: theme.palette.primary.light,
},
form: {
width: '100%', // Fix IE 11 issue.
marginTop: theme.spacing(1),
},
submit: {
margin: theme.spacing(3, 0, 2),
backgroundColor: theme.palette.primary.light,
},
});
class SignIn extends React.Component {
state = {
email: '',
password: '',
};
static propTypes = {
login: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool,
};
onSubmit = (e) => {
e.preventDefault();
this.props.login(this.state.email, this.state.password);
};
onChange = (e) => this.setState({ [e.target.name]: e.target.value });
render() {
const { classes } = this.props;
const { email, password } = this.state;
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Sign in
</Typography>
<form className={classes.form} onSubmit={this.onSubmit}>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
id="email"
label="Email Address"
name="email"
autoComplete="email"
autoFocus
onChange={this.onChange}
value={email}
/>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
name="password"
label="Password"
type="password"
id="password"
autoComplete="current-password"
onChange={this.onChange}
value={password}
/>
<FormControlLabel
control={<Checkbox value="remember" color="primary" />}
label="Remember me"
/>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
>
Sign In
</Button>
<Grid container>
</Grid>
</form>
</div>
</Container>
);
}
}
const mapStateToProps = (state) => ({
isAuthenticated: state.auth.isAuthenticated,
});
export default (withStyles(styles)(SignIn));
authreducer.js
import {
USER_LOADED,
USER_LOADING,
AUTH_ERROR,
LOGIN_SUCCESS,
LOGIN_FAIL,
LOGOUT_SUCCESS,
REGISTER_SUCCESS,
REGISTER_FAIL,
} from '../actions/types';
const initialState = {
token: localStorage.getItem('token'),
isAuthenticated: null,
isLoading: false,
user: null,
};
export default function (state = initialState, action) {
switch (action.type) {
case USER_LOADING:
return {
...state,
isLoading: true,
};
case USER_LOADED:
return {
...state,
isAuthenticated: true,
isLoading: false,
user: action.payload,
};
case LOGIN_SUCCESS:
case REGISTER_SUCCESS:
localStorage.setItem('token', action.payload.token);
return {
...state,
...action.payload,
isAuthenticated: true,
isLoading: false,
};
case AUTH_ERROR:
case LOGIN_FAIL:
case LOGOUT_SUCCESS:
case REGISTER_FAIL:
localStorage.removeItem('token');
return {
...state,
token: null,
user: null,
isAuthenticated: false,
isLoading: false,
};
default:
return state;
}
}
authactions.js
import axios from 'axios';
import {
USER_LOADED,
USER_LOADING,
LOGIN_SUCCESS,
LOGOUT_SUCCESS,
REGISTER_SUCCESS,
} from './types';
// CHECK TOKEN & LOAD USER
export const loadUser = () => (dispatch, getState) => {
// User Loading
dispatch({ type: USER_LOADING });
axios
.get('http://localhost:8000/api/auth/user', tokenConfig(getState))
.then((res) => {
dispatch({
type: USER_LOADED,
payload: res.data,
});
})
.catch((err) => {
console.log(err)
});
};
// LOGIN USER
export const login = (email, password) => (dispatch) => {
// Headers
const config = {
headers: {
'Content-Type': 'application/json',
},
};
const body = JSON.stringify({ email, password });
axios
.post('http://localhost:8000/api/auth/login', body, config)
.then((res) => {
dispatch({
type: LOGIN_SUCCESS,
payload: res.data,
});
})
.catch((err) => {
console.log(err.response.data)
});
};
// REGISTER USER
export const register = ({ username, password, email }) => (dispatch) => {
// Headers
const config = {
headers: {
'Content-Type': 'application/json',
},
};
// Request Body
const body = JSON.stringify({ username, email, password });
axios
.post('http://localhost:8000/api/auth/register', body, config)
.then((res) => {
dispatch({
type: REGISTER_SUCCESS,
payload: res.data,
});
})
.catch((err) => {
console.log(err.response.data)
});
};
// LOGOUT USER
export const logout = () => (dispatch, getState) => {
axios
.post('http://localhost:8000/api/auth/logout/', null, tokenConfig(getState))
.then((res) => {
dispatch({ type: 'CLEAR_LEADS' });
dispatch({
type: LOGOUT_SUCCESS,
});
})
.catch((err) => {
console.log(err.response.data)
});
};
// Setup config with token - helper function
export const tokenConfig = (getState) => {
// Get token from state
const token = getState().auth.token;
// Headers
const config = {
headers: {
'Content-Type': 'application/json',
},
};
// If token, add to headers config
if (token) {
config.headers['Authorization'] = `Token ${token}`;
}
return config;
};
Based on my experience, this error often occurs due to the fact that one of the packages in package.json (often it's React) is in dependencies (not in devDependencies). And this version conflicts with yours.
I am beginner in react native. I want to rename the image before uploading to AWS s3. Till uploading to AWS s3 I have done. But I am not getting how to rename the image before uploading.
For example, my captured image name is : image-3bcf276c-cab1-4400-83a6-8f92971977c8.jpg . I want to change it like : b514c19662714234_geolocation_222222222222222.png
How can I do this.
My file uploading code is,
import React, {Component} from 'react';
import {Platform, StyleSheet,Alert, Text,TouchableOpacity, View} from 'react-native';
import { Dropdown } from 'react-native-material-dropdown';
import ImagePicker from 'react-native-image-picker';
import DeviceInfo from 'react-native-device-info';
import { RNS3 } from 'react-native-aws3';
export default class App extends Component<Props> {
static navigationOptions = {
title: 'Check In',
};
constructor(props) {
super(props);
this.state = {
deviceId: '',
isCameraVisiable: false,
latitude: null,
longitude: null,
error: null,
serverTime: null,
file :'',
saveImages : []
}
}
handleSubmit(){
var date = new Date();
var time = date.getTime();
console.log(time);
// var data = {}
// data['time'] = time;
Alert.alert('You cliked on send!');
}
getServerTime() {
fetch('http://worldclockapi.com/api/json/utc/now')
.then((response) => response.json())
.then((responseJson) => {
if (responseJson) {
this.setState({
serverTime: responseJson
})
}
console.log(responseJson);
})
.catch((error) => {
console.error(error);
});
}
componentDidMount() {
navigator.geolocation.getCurrentPosition(
(position) => {
this.setState({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
error: null,
});
},
(error) => this.setState({ error: error.message }),
{ enableHighAccuracy: true, timeout: 20000 },
);
console.log(this.state.longitude);
console.log(this.state.error);
}
takePic(){
ImagePicker.launchCamera({},(responce)=>{
console.log(responce);
const file ={
uri : responce.uri,
name : responce.fileName,
method: 'POST',
path : responce.path,
type : responce.type,
notification: {
enabled: true
}
}
console.log('fffffffffffffffffffffffffffffffffffffffffffffff');
console.log(file);
this.state.saveImages.push(file);
console.log('=============********************================');
console.log(this.state.saveImages);
})
}
_upload=(saveImages)=>{
const config ={
keyPrefix :'uploads/',
bucket : 's3merahkee',
region :'***********',
accessKey:'************',
secretKey :'**************',
successActionStatus :201
}
this.state.saveImages.map((image) => {
RNS3.put(image,config)
.then((responce) => {
console.log('=============********************================');
console.log(saveImages);
Alert.alert('You cliked on send!');
});
});
}
render() {
return (
<View style={styles.container}>
<View style={styles.Camera}>
<TouchableOpacity onPress={this.takePic.bind(this)}>
<Text>Take Picture</Text>
</TouchableOpacity>
</View>
<View style={styles.Send}>
<TouchableOpacity onPress={() => this._upload()}>
<Text>Send</Text>
</TouchableOpacity>
<View>
<Text>Latitude: {this.state.latitude}</Text>
<Text>Longitude: {this.state.longitude}</Text>
<Text>DeviceUniqueId: {DeviceInfo.getUniqueID()}</Text>
{this.state.error ? <Text>Error: {this.state.error}</Text> : null}
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
Dropdown :{
},
Camera :{
justifyContent: 'center',
alignItems: 'center',
marginTop : 20,
backgroundColor : '#48a4ff',
alignItems : 'center',
padding : 1,
borderWidth : 1,
borderColor : '#48a4ff',
},
Send :{
justifyContent: 'center',
alignItems: 'center',
marginTop : 20,
backgroundColor : '#48a4ff',
alignItems : 'center',
padding : 3,
borderWidth : 1,
borderColor : '#48a4ff',
}
});
I have done it in a simple way.Instead of writing name : responce.fileName, I mentioned which name I want to give to it i.e
name : DeviceInfo.getUniqueID()+'_'+this.state.latitude+'_'+this.state.longitude+'.jpg',
//simply name='new_image.png';