React-native Geolocation. Wait for user's response to request - react-native

iOS issue only:
I am using the following code to get the users location.
navigator.geolocation.getCurrentPosition(
(position) => {
console.log("Native GEO GOOD", position);
return resolve(position)
},
(err) => {
console.log("Native GEO BADD", err);
return reject(err)
},
{ enableHighAccuracy: false, timeout: 5000, maximumAge: 0 },
)
The above code opens a dialog box, from which the user can allow my app to geolocate.
The problem is I want to wait until the user actually responds using the dialog box before calling the error or success callback.
I tried to use: requestAuthorization(). But that just opens the dialog box and I have no way to telling when the user has accepted the request to geolocate.
What I would like to do is ask the users permission to geolocate, then after the user accepts, try to geolocate the user.
But I don't see how to do that using react-native geolocation.
If requestAuthorization() took a callback option for when the user responds to the dialog box, that would solve my issue.

In React-Native using Expo (https://expo.io) you ask for permissions using a Promise and then act on the promise (hopefully when permission is given).
Permissions.askAsync((Permissions.LOCATION)
.then(({status}) => {
//your code here after permission is granted
});
);
If you aren't using expo, there is a Component call react-native-permissions (https://github.com/yonahforst/react-native-permissions.git) that allows you to request permissions using a promise like my example but without expo. Their example shows the request setting state to let you know the permissions status which you can act on.
Permissions.request('location', { type: 'always' }).then(response => {
this.setState({ locationPermission: response })
})

Related

Expo - How to make custom permission message

I need to change the camera message when I try to reach user's camera.
It returns default "Allow xx to use your camera" message and I want to change It with my own permission message.
Where to change It and how?
Here's my camera code below.
useEffect(() => {
(async function () {
const { status } = await Camera.requestPermissionsAsync()
setHasPermission(status === 'granted')
})()
}, [])
From the Expo documentation:
To request permissions on iOS, you have to describe why the permissions are requested and install the library that can request this permission. In the managed workflow, you can do that by customizing the ios.infoPlist property in your app.json file. When using the bare workflow, you have to edit the info.plist file directly.
So, in app.json:
"infoPlist": {
"NSCameraUsageDescription": "This app uses the camera to scan barcodes on event tickets."
}
In a non-managed workflow, you can edit NSCameraUsageDescription in your Info.plist for the app in Xcode.

Expo AuthSession immediately resolves as "dismiss"ed

I'm using Expo's AuthSession module to sign into Auth0:
let result = await AuthSession.startAsync({
authUrl: `${auth0Domain}/authorize?` + qs.stringify({
client_id: auth0ClientId,
response_type: 'code',
scope: 'openid profile email offline_access',
redirect_uri: redirectUrl,
code_challenge_method: 'S256',
code_verifier: codeVerifier,
state: oAuthState,
audience: auth0Audience
})
})
if (result.type === 'success') {
...
} else if (
result.type === 'dismiss' ||
(result.type === 'error' && result.errorCode === 'login-declined')
) {
// FIXME: alert never pops without delay here, despite the if correctly evaluating true
// Any way to check if the window has closed, if that's the issue?
await new Promise((resolve) => setTimeout(resolve, 5000))
let retry = await alertAsync(
'Authentication dismissed',
'Cancel signing in?',
'Try again',
'Exit'
)
return retry ? this.loginWithAuth0() : false
}
logger.error('Login failed', result)
throw new Error('Error signing in')
}
In a dev environment I never have an issue, but in a published app the promise often resolves immediately with { type: dismiss }, before the browser closes. Once I caught a glimpse of an alert (that I try to popup in case the session is actually dismissed) even before the AuthSession window opened.
If I add a 5s delay before popping the error up, it does show up 5s after starting the AuthSession rather than it finishing.
Testing on Android.
I looked into migration over to WebBrowser but it seems to be the same implementation.
Is there a consistent way to actually await the Auth process finishing, or should I ditch this whole thing and write my own login screen?
Thanks in advance for any input / direction!
I've put a detailed explanation of the problem and a possible solution https://github.com/expo/expo/issues/6679#issuecomment-570963717
As a temporary workaround, you can call AppState.currentState somewhere in your app, maybe logging it just to add the right listeners before the user clicks and enable the AuthSession (read more in the linked GitHub issue).
Have a look and comment please :)

problem accessing elements using id/name for login form in cypress

I am trying to login to a form written in angular js but cypress throws the following exception:
Uncaught TypeError: $(...).materialScrollTop is not a function
This error originated from your application code, not from Cypress.
When Cypress detects uncaught errors originating from your application it will automatically fail the current test.
This behavior is configurable, and you can choose to turn this off by listening to the 'uncaught:exception' event.
https://on.cypress.io/uncaught-exception-from-application
This is the cypress login code:
context('TestLogin', () => {
it('Test Login', () => {
cy.visit('url');
cy.get('input[id=Email]').type('email', {force: true});
cy.get('input[id=Password]').type('passcode', { force: true });
cy.get('button[type=submit]').click();
})
})
Since the login has a csrf token, I have used cy.request() as follows and I do get a response with status code 200 but when re-loading the site it goes back to login page.
describe("Tests for AntiForgeryToken", function () {
// variable from config, that contain Identity Server URL
const identityUrl = Cypress.config("identityServerUrl")
// command declaration that we are going to use in tests
// allows us to create request to server
Cypress.Commands.add("loginByToken", function (token, login, password) {
cy.request({
method: "POST",
failOnStatusCode: false,
url: `${identityUrl}/Account/Login`,
form: true,
body: {
email: login,
password: password,
__RequestVerificationToken: token,
RememberLogin: false
}
})
})
it("Should parse token from response body and return 200", function () {
cy.request(`${identityUrl}/Account/Login`)
.its("body")
.then((body) => {
const $html = Cypress.$(body)
// when the page is rendered
// we are trying to find the Request Token in the body of page
const token = $html.find("input[name=__RequestVerificationToken]").val()
// POST request with token and login data
// then we simply verify whether Indentity Server authorized us
cy.loginByToken(token, "test#test.com", "Test_1234")
.then((resp) => {
expect(resp.status).to.eq(200)
})
})
cy.visit(`${identityUrl}/Account/`);
})
Cypress documentation didn't provide much info about the exception.
Any insights from cypress experts are helpful.
As evident from the error, Cypress is failing the test as it found an exception in your application,this is not a cypress level exception but an uncaught exception in your app which is causing cypress to fail the test, this is pretty useful as you can check if its an actual error in your app and log it for the dev team to fix, check if you are able to reproduce this manually, either way i think the application code should be fixed to either fix the bug or catch the exception and return a valuable error message. If you want to disable this feature you can turn off all uncaught exception handling, so in your index.js or whatever file is the entry point add the following:
Cypress.on('uncaught:exception', (err, runnable) => {
// returning false here prevents Cypress from
// failing the test
// you can also add a Debugger here to analyze the error
debugger;
return false;
});
not sure if turning this off will help as looks like there is something in your application which could be an issue, but this is just for informational purposes that you can turn this feature off if you needed to.
Here is the documentation for further reading : Cypress Events documentation
hope this helps

Send push notifications to a specific logged user with pinpoint

Im developing a react native app integrated with aws-amplify and aws mobile hub.
We currently logged our user into cognito.
im trying to send a push notification to a specific endpoint Id, but i cannot see how to set that endpointId, in the node js (im using lambda) documentation for pinpoint only exist getEnpoint, but requires EnpointId and i cannot find where is it.
app.post('/asdf/get/endpoint', function (req, res) {
pinpoint.getEndpoint({
ApplicationId: APP_ID_PINPOINT,
EndpointId: req.body.userId
}, (err, data) => {
if (err) {
res.json({ error: err });
} else {
res.json({ data: data });
}
});
});
How can set the enpointId (userId) to an already logged user and then, every time i need to send a push to him i just get that enpoint and send a push to all the devices attached to it?
Or, i have to store all the tokens from the user (DynamoDb), and send messages to all the devices separately? (with pinpoint sendMessage)

Retrying Geolocation after error in React Native

I am using React Native's Geolocation API to get user's location or ask user to turn location on:
// Handle PermissionsAndroid
this.watchID = navigator.geolocation.watchPosition(
(position) => {
// Update position
},
(error) => {
switch (error.code)
{
Case 1: {
// Ask user to turn on Location (Permission has already been asked for)
}
}
}
);
Now, I want to retry the watchPosition if user ever turned location on at some later point.
Using AppState, I tried getting an event if user started to interact with the notification bar (maybe user is trying to turn on Location). But it only calls back if application is sent to background or is activated again (but not during notification bar interactions).
Since Geolocation conforms with W3 standards, I tried searching for solutions in web development world. But the only solution that I found, was using iFrame which is browser-only.
Also, a non-elegant solution would be to setInterval (say every 5 seconds) and then clearInterval only if a position has been returned.
Is there a proper way to do this?
You should initiate the flow by getting the current location first, then create the watch. The following is from the Geolocation docs.
navigator.geolocation.getCurrentPosition(
(position) => {
if (position) {
//Handle first position
}
},
(error) => (console.log(error)),
{enableHighAccuracy: true, timeout: 20000, maximumAge: 1000}
);
this.watchID = navigator.geolocation.watchPosition((position) => {
if (position) {
// Handle new position
}
});
I've found this recovers from location errors itself, as well as handling permission requests (tested on iOS 10).