Possible Unhandled Promise Rejection (id:0) TypeError: undefined is not an object (evaluating 'ImagePicker.Permissions.askAsync) - react-native

I am building an iOS app using React-Native and Expo. I have figured out how to access the user's Photo Library but am unable to request permissions from their device. It instead just accesses the camera roll without asking, which is obviously not allowed in iOS. Below is some of my code:
import React, {useState, useEffect, useRef} from 'react';
import * as ImagePicker from 'expo-image-picker';
import Permissions, {usePermissions} from 'expo-permissions';
import Constants from 'expo-constants'
//The useEffect function is what is ran when checking to see if the async function has been accepted or not
const Screen = ({navigation}) => {
const [ imagePicker1, setImagePicker1 ] = useState(null);
useEffect(() => {
const permission_get1 = async ()=> {
if (Platform.OS !== 'web'){
let { status } = await ImagePicker.Permissions.askAsync(Permissions.MEDIA_LIBRARY);
if (status !== 'granted'){
console.log('No permission given')
alert('Camera roll required for to upload photo from your library');
return;
}
/*if (status === 'granted'){
console.log('Permission granted')*/
let imagePicker1 = await ImagePicker.getMediaLibraryAsync()
setImagePicker1(imagePicker1)
console.log('It worked')
}
};
permission_get1();
}, []);

I ended up getting it to work using this: I believe the previous example was using a depreciated method.
import * as ImagePicker from 'expo-image-picker'
const pickImage = async ()=>{
const { granted } = await Permissions.askAsync(Permissions.CAMERA_ROLL)
if(granted){
console.log('access granted')
let data = await ImagePicker.launchImageLibraryAsync({
mediaTypes:ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect:[1,1],
quality:0.5,
})
console.log(data)
if (!data.cancelled){
setImage(data.uri);
}
}else{
Alert.alert('Permissions required to access camera roll.')
}
}

Related

Voximplant push notifications - React Native

I have successfully managed to get remote messages from Firebase console to send messages when my app is in the QUIT state, FOREGREOUND state and in BACKGROUND state. So all the Firebase setup is working PERFECTLY in my React Native app (Android).
I managed to get Firebase Push Notifications working very well, using the latest Firebase messaging library.
The Version 5 'react-native-firebase' is deprecated and no longer supported, we have to use Version 6 '#react-native-firebase/messaging' for messaging and 'react-native-push-notification' (for the channel stuff and notification). This is good for the future in all React Native work, as the old version is becoming obsolete and Firebase isn't supporting it.
The google-services.json works fine and the push certificate is also successfully uploaded in the Voximplant panel.
I can already make calls when both Voximplant users are on the app (i.e. app states are in the FOREGROUND).
The MAIN reason for needing the push, is to allow another Voximplant user to receive a call from the app QUIT and BACKGROUND state.
What seems to be missing?
Here's my code:
package.json
"#react-native-firebase/app": "^15.2.0",
"#react-native-firebase/messaging": "^15.2.0",
"react-native-voximplant": "^1.28.0",
index.js
import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';
import messaging from '#react-native-firebase/messaging';
import PushBackground from './manager/PushBackground';
//Set handler
messaging().setBackgroundMessageHandler(PushBackground);
// NOT USING (but still works with pure firebase incoming push notifications)
//messaging().setBackgroundMessageHandler(async remoteMessage => {
// console.log('Message handled in the background!', remoteMessage);
//});
AppRegistry.registerComponent(appName, () => App);
App.js
import React, { useState, useEffect } from "react";
import { requestUserPermission, NotificationListener, CancelNotifications } from "./manager/PushManager";
function App() {
useEffect(() => {
//Firebase messaging/notifications
requestUserPermission(); //gets "fcmtoken"
NotificationListener(); //sets up firebase listeners for (QUIT, FOREGROUND, BACKGROUND App states)
CancelNotifications(); //Cancels notifications after received
}, []);
return(...all UI stuff...)
export default App;
}
loginScreen.js
import React, { Component, useEffect, useState, useContext } from "react";
import { Voximplant } from 'react-native-voximplant';
import { APP_NAME, ACC_NAME } from "../../constant/constants";
import AsyncStorage from '#react-native-async-storage/async-storage';
const LoginScreen = ({ navigation }) => {
const voximplant = Voximplant.getInstance();
useEffect(() => {
const connect = async() => {
const status = await voximplant.getClientState();
if(status == Voximplant.ClientState.DISCONNECTED){
await voximplant.connect();
console.log("Status: Voximplant Client DISCONNECTED: " + status);
}
else if (status == Voximplant.ClientState.LOGGED_IN) {
console.log("Status: Voximplant Client LOGGED IN: " + status);
}
const signIn = async () => {
try{
const fqUsername = `${username}#${APP_NAME}.${ACC_NAME}.voximplant.com`;
console.log(fqUsername);
let authResult = await voximplant.login(fqUsername, password);
const loginTokens = authResult.tokens;
console.log("Log In Successful. Token " + JSON.stringify(loginTokens, null, 2));
//We have fcmtoken (firebase) from App.js loading up, so now register it with Voximplant cloud
const fcmToken = await AsyncStorage.getItem("fcmtoken");
//Register it with Voximplant
if(fcmToken != null){
voximplant.registerPushNotificationsToken(fcmToken);
}
//Set fqUsername for Voximplant
await AsyncStorage.setItem("fqUsernameKey", fqUsername);
//Set accessToken for Voximplant
await AsyncStorage.setItem('accessTokenKey', authResult.tokens.accessToken);
}
catch(e) {
console.log("signIn issue: "+ e);
Alert.alert("Already Logged In", `Error code: ${e.code}`);
}
}
return(...UI stuff...)
}
export default LoginScreen;
PushBackground.js
'use strict';
import AsyncStorage from '#react-native-async-storage/async-storage';
import { Voximplant } from 'react-native-voximplant';
export default async (message) => {
console.log('PushBackground android: notification: ' + JSON.stringify(message));
const username = await AsyncStorage.getItem("fqUsernameKey"); //THIS IIS SET IN LOGINSCREEN.JS
const accessToken = await AsyncStorage.getItem("fcmtoken"); //THIS IS SET IN PUSHMANAGER.JS
const client = Voximplant.getInstance();
await client.loginWithToken(username, accessToken);
await client.handlePushNotification(message.data);
return Promise.resolve();
};
PushManager.js
import AsyncStorage from '#react-native-async-storage/async-storage';
import messaging from "#react-native-firebase/messaging";
import PushNotification, {Importance} from 'react-native-push-notification';
async function getFCMToken() {
let fcmtoken = await AsyncStorage.getItem("fcmtoken");
console.log(fcmtoken, "old token")
if(!fcmtoken){
try{
const fcmtoken = await messaging().getToken();
if(fcmtoken) {
console.log(fcmtoken, "new token");
await AsyncStorage.setItem("fcmtoken", fcmtoken);
}
}
catch (error) {
console.log(error, "error in fcmtoken")
}
}
}
//Create Push Notification channel
async function createVoximplantChannel() {
PushNotification.createChannel( //Haven't tried with Voximplant
{
channelId: "voximplant_channel_id", // "channel-id", // (required)
channelName: "Incoming call channel", //"My channel", // (required)
channelDescription: "Incoming call received", //"A channel to categorise your notifications", // (optional) default: undefined.
playSound: true, // (optional) default: true
soundName: "default", // (optional) See `soundName` parameter of `localNotification` function
importance: 5, // (optional) default: 4. Int value of the Android notification importance. importance: 2 <- SILENT NOTIFICATION CHANNEL & importance: 5 <- POPUP NOTIFICATION CHANNEL
vibrate: false, // (optional) default: true. Creates the default vibration patten if true.
},
(created) => console.log(`createChannel returned '${created}'`) // (optional) callback returns whether the channel was created, false means it already existed.
);
}
export async function requestUserPermission() {
const authStatus = await messaging().requestPermission();
const enabled =
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
if (enabled) {
console.log('Authorization Status: ', authStatus);
getFCMToken();
createVoximplantChannel();
}
}
export const NotificationListener = () => {
//******** Notification from BACKGROUND state app ********
messaging().onNotificationOpenedApp((remoteMessage) => {
PushNotification.localNotification({
channelId: "voximplant_channel_id", //remoteMessage.notification.android.channelId, //Perhaps "voximplant_channel_id" ?
message: remoteMessage.notification.body,
title: remoteMessage.notification.title,
//bigPictureUrl: remoteMessage.notification.android.imageUrl,
//smallIcon: remoteMessage.notification.android.imageUrl,
vibrate: true,
data: remoteMessage.data,
})
});
//******** Notification from QUIT state app ********
messaging()
.getInitialNotification()
.then( (remoteMessage) => {
PushNotification.localNotification({
channelId: "voximplant_channel_id", //remoteMessage.notification.android.channelId, //Perhaps "voximplant_channel_id" ?
message: remoteMessage.notification.body,
title: remoteMessage.notification.title,
//bigPictureUrl: remoteMessage.notification.android.imageUrl,
//smallIcon: remoteMessage.notification.android.imageUrl,
vibrate: true,
data: remoteMessage.data,
})
});
//******** Notification from FOREGROUND state app ********
messaging().onMessage(async (remoteMessage) => {
PushNotification.localNotification({
channelId: "voximplant_channel_id", //remoteMessage.notification.android.channelId,
message: remoteMessage.notification.body,
title: remoteMessage.notification.title,
//bigPictureUrl: remoteMessage.notification.android.imageUrl,
//smallIcon: remoteMessage.notification.android.imageUrl,
vibrate: true,
data: remoteMessage.data,
})
});
}
//Cancel notifications (so hopefully no overlapping notificatons)
export const CancelNotifications = () => {
PushNotification.cancelAllLocalNotifications();
};

React Native expo-permission deprecated what to use now?

I am using Permissions from the expo-permission Library to get the location coords of the user:
import * as Location from "expo-location";
import * as Permissions from 'expo-permissions';
const granted = await Permissions.askAsync(Permissions.LOCATION);
The above works but keeps giving the warning that expo-permissions is deprecated.
If I use:
import {Location, Permissions } from 'expo';
it says Cannot read property 'askAsync' of undefined.
Does someone know what i should use? I use sdk42
Thx!
As this blog by Brent Vatne says,
expo-permissions has been deprecated in favor of module-specific permissions methods You should migrate from using
Permissions.askAsync and Permissions.getAsync to the permissions
methods exported by modules that require the permissions.
For example: you should replace calls to
Permissions.askAsync(Permissions.CAMERA) with
Camera.requestPermissionsAsync()
There shouldn’t be two ways to do an identical thing in a single SDK,
and so we picked our preferred approach and are consolidating around
it.
So now, you will have to use Permissions from individual packages
For Location,
Firstly, install expo-location
expo install expo-location
Then you can use it like this
import * as Location from 'expo-location';
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
console.log('Permission to access location was denied');
return;
}
If someone comes here and wants to get permissions for ImagePicker, then according to the docs you should do this:
import * as ImagePicker from "expo-image-picker";
const getPermissionAsync = async () => {
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (status !== "granted") {
alert("...");
}
};
Now with expo, each libs have their own permissions requests methods.
Example with Location:
let { status } = await Location.requestForegroundPermissionsAsync();
Documentation
works:
import { Camera } from 'expo-camera';
import * as ImagePicker from "expo-image-picker";
const resultPermision = await Camera.requestCameraPermissionsAsync();
const resultPermisionCamera = resultPermision.status;
if (resultPermisionCamera === "denied") {
toastRef.current.show("Gallery permissions are needed");
} else {
const result = await ImagePicker.launchImageLibraryAsync({
allowsEditing: true,
aspect: [4, 3]
})
........
Now, expo-camera works great, but also I believe something is pending on the side of the app.json.
Do you still need to add these lines?:
"permissions": [
"CAMERA",
"WRITE_EXTERNAL_STORAGE",
"CAMERA_ROLL"
question for everyone here :)
import { Camera } from "expo-camera";
this.state = {
hasCameraPermission: null,
type: Camera.Constants.Type.back,
};
componentDidMount = async () => {
const { status } = await Camera.requestCameraPermissionsAsync();
this.setState({ hasCameraPermission: status === "granted" });
};

React Native Expo Location Permissions, requestForegroundPermissionsAsync, requestBackgroundPermissionsAsync how make them work

I am working on an app that records the location when the aplication is active or in the background.
After Permissions.getAsync got depricated, my app is not recording the coordonates when in background.
I understand I have to change to requestForegroundPermissionsAsync and requestBackgroundPermissionsAsync but I can not make it work.(I don't know how to use requestBackgroundPermissionsAsync)
Here is the code:
import { useState, useEffect } from 'react';
import {
Accuracy,
requestForegroundPermissionsAsync,
requestBackgroundPermissionsAsync,
watchPositionAsync
} from 'expo-location';
export default (shouldTrack, callback) => {
const [err, setErr] = useState(null);
useEffect(() => {
let subscriber;
const startWatching = async () => {
try {
const { granted } = await requestForegroundPermissionsAsync()
if (!granted) {
throw new Error('Location permission not granted');
}
subscriber = await watchPositionAsync(
{
accuracy: Accuracy.BestForNavigation,
timeInterval: 1000,
distanceInterval: 10
},
callback
);
} catch (e) {
setErr(e);
}
};
Question needs additional information to troubleshoot. For starters, are you on iOS\Android\both? Does it work on one platform but not the other? What permissions are listed in app.json (is this a managed\bare workflow)? Are the foreground updates working? Background updates require additional code and none of that is included here.
Have you gone through Expo's documentation? https://docs.expo.io/versions/latest/sdk/location

React Native with expo Error : useEffect read-only

I tried two steps and i got same error (useEffect read-only):
const requestPermission = async ()=> {
const {granted}= await ImagePicker.requestCameraRollPermissionsAsync;
if(!granted)
alert('You have to enable permission to access photos');
};
useEffect =(()=> {
requestPermission();
},[])
and the second way is
useEffect =(()=> {
const request = async ()=>{
const {granted}= await ImagePicker.requestCameraRollPermissionsAsync;
if(!granted)
alert('You have to enable permission to access photos');
};
},[])
Thank you in advance.
You have an = sign in your useEffect calls (right after useEffect) that is making it look like you're assigning something to useEffect
Edited:
useEffect(() => {
requestPermission();
}, [])
You would have to edit your other call in the same way.

how to handle failed silent auth error in auth0

I followed spa react quick start guide and it worked fine for more than a month. Recently i had this error and it is logged on auth0 as 'failed silent error' with no further information. I have been told that it is because of the browsers cookie updates and recommended to use new beta release of auth0-spa-js and change cache location to local storage. And it didn't work either.
The code is as follows:
auth_config.json:
{
"domain": "dev.........eu.auth0.com",
"clientId": "....eEKkQ.............",
"redirect_uri": "https://localhost:8080",
"audience": "https://.......herokuapp.com/v1/....",
"cacheLocation": "localstorage"
}
and
react-auth0-wrapper.js:
import React, { useState, useEffect, useContext } from "react";
import createAuth0Client from "#auth0/auth0-spa-js";
const DEFAULT_REDIRECT_CALLBACK = () =>
window.history.replaceState({}, document.title, window.location.pathname);
export const Auth0Context = React.createContext();
export const useAuth0 = () => useContext(Auth0Context);
export const Auth0Provider = ({
children,
onRedirectCallback = DEFAULT_REDIRECT_CALLBACK,
...initOptions
}) => {
const [isAuthenticated, setIsAuthenticated] = useState();
const [user, setUser] = useState();
const [auth0Client, setAuth0] = useState();
const [loading, setLoading] = useState(true);
const [popupOpen, setPopupOpen] = useState(false);
useEffect(() => {
const initAuth0 = async () => {
const auth0FromHook = await createAuth0Client(initOptions);
setAuth0(auth0FromHook);
if (window.location.search.includes("code=")) {
const { appState } = await auth0FromHook.handleRedirectCallback();
onRedirectCallback(appState);
}
const isAuthenticated = await auth0FromHook.isAuthenticated();
setIsAuthenticated(isAuthenticated);
if (isAuthenticated) {
const user = await auth0FromHook.getUser();
setUser(user);
}
setLoading(false);
};
initAuth0();
// eslint-disable-next-line
}, []);
const loginWithPopup = async (params = {}) => {
setPopupOpen(true);
try {
await auth0Client.loginWithPopup(params);
} catch (error) {
console.error(error);
} finally {
setPopupOpen(false);
}
const user = await auth0Client.getUser();
setUser(user);
setIsAuthenticated(true);
};
const handleRedirectCallback = async () => {
setLoading(true);
await auth0Client.handleRedirectCallback();
const user = await auth0Client.getUser();
setLoading(false);
setIsAuthenticated(true);
setUser(user);
};
return (
<Auth0Context.Provider
value={{
isAuthenticated,
user,
loading,
popupOpen,
loginWithPopup,
handleRedirectCallback,
getIdTokenClaims: (...p) => auth0Client.getIdTokenClaims(...p),
loginWithRedirect: (...p) => auth0Client.loginWithRedirect(...p),
getTokenSilently: (...p) => auth0Client.getTokenSilently(...p),
getTokenWithPopup: (...p) => auth0Client.getTokenWithPopup(...p),
logout: (...p) => auth0Client.logout(...p)
}}
>
{children}
</Auth0Context.Provider>
);
};
What is wrong with this code, any help appreciated. Or i can use a different method, i just followed the docs, it doesn't matter as long as it authenticates.
Thanks
I know this has been hanging around for a bit, but i was running into a similar issue.
As I understand it the createAuth0Client helper factory runs the getTokenSilently function by default as part of the set up to re-authenticate users every browser refresh. The problem i was having was that the call to getTokenSilently was erroring, meaning that auth0FromHook was never set and the auth0client never set in state. Because auth0client was undefined, it was then impossible to call loginwithredirect, which is the behaviour i wanted to achieve.
Basically i wanted it to auth silently, but if it failed, send to the log in screen, but that's impossible because the auth0client was undefined, resulting in a cannot call loginwithredirect of undefined error. It seems that (sadly) in the current stable version of the #auth0/auth0-spa-js library (1.6.5 at time of writing) there is no way to bypass getTokenSilently when initialising the client. However in the current beta (1.7.0-beta.5) (Here is a list of versions) they have exposed the Auth0Client class itself, so if you want to move to that version the code could be tweaked with something like....
initAuth0().catch( e => {
const newClient = new Auth0Client(initOptions);
setAuth(newClient);
})
and then in any protected components you can check the loading is finished and if isAuthenticated is still falsey, you should be able to redirect to login despite an error occurring during the getSilentToken.
== NON BETA OPTION
The alternative in the current api would be to perhaps set max_age to 0 or 1 in the initOptions, to force a re-login, and maybe setting prompt to "login" on the second attempt to initialize the authClient