How to get user location in react-native - geolocation

I want to display user current location. I am able to get the latitude and longitude of the user. Then I am using react-native-geocoding to get user location place name. But I am getting error when doing it.
Below is my code:
const getCity = () => {
console.log("get city func");
Geocoder.init("MY_API_KEY");
Geocoder.from(28.6139, 77.2090).then(json => {
var addressComponent = json.results[0].address_components[0];
console.log(addressComponent);
})
.catch(error => console.warn(error));
}
<TouchableOpacity onPress={getCity}>
<Text style={styles.title}>Location Screen</Text>
</TouchableOpacity>
I am getting error:
code: 4, message: "Error from the server while geocoding. The receive…s 'origin' field. Check it for more informations.", origin: {…}}
code: 4
message: "Error from the server while geocoding. The received datas are in the error's 'origin' field. Check it for more informations."
origin:
error_message: "This API project is not authorized to use this API."
results: []
status: "REQUEST_DENIED"
__proto__: Object
__proto__: Object
I don't know what I am doing wrong here. Also if there is any other way to get the user's location name then please give suggestions.

Looks like an authorization problem, related to an invalid key. Try calling your Geocoder.init(API_KEY), at the root level of the file or application, not in the function definition itself.
Geocoder.init("MY_API_KEY");
const getCity = () => {
console.log("get city func");
Geocoder.from(28.6139, 77.2090).then(json => {
var addressComponent = json.results[0].address_components[0];
console.log(addressComponent);
})
.catch(error => console.warn(error));
}
<TouchableOpacity onPress={getCity}>
<Text style={styles.title}>Location Screen</Text>
</TouchableOpacity>

Related

Not able to add friendly name to participants when creating participant with conversation

I am using twilio for creating an chat API using conversion API (Twilio), I was able to create the converstaion and add new participant to the converstion, But when I try to add friendly name to participant, Its not adding.
client.conversations.conversations(conversionsSID)
.participants
.create({
identity: identity,
FriendlyName: name,
attributes: JSON.stringify({
profileImage: profileImage
})
}).then((participant) => {
resolve({ participant: participant, error: null })
}).catch((error) => {
reject({ participant: null, error: error });
});
I have tried with FriendlyName and friendly_name, both of that does't work.
The participant resource does not have a FriendlyName property. You can see the available properties that you can use in the documentation for creating a conversation participant.
You are already using the attributes property to store a profile image, so you could use this to store your friendly name too. So, you could change your code to:
client.conversations.conversations(conversionsSID)
.participants
.create({
identity: identity,
attributes: JSON.stringify({
name: name,
profileImage: profileImage
})
}).then((participant) => {
resolve({ participant: participant, error: null })
}).catch((error) => {
reject({ participant: null, error: error });
});
You can do like this:
At first CreateConversation after that call update to the user
Client.conversations.users("US1f1ff79756794a28b55fbb1c8ac2b150").fetch().then((user) => {
user.update({ friendlyName: "r2d2" })
.then()
.catch(error => {
console.log(error);
});
});
cheers!

Firestore: Could not reach Cloud Firestore backend. Connection failed 1 times

I have a Firebase real-time database integrated with React Native where I can create users. But my problem is that when I try to log in I get an error. I run my app on IOS if it makes any difference. I have followed this guide: How to Build a React Native App and Integrate It with Firebase. I use the method Email/Password for login. I use Firebase version 9.6.4.
I have looked through this thread: Could not reach Cloud Firestore backend. and tried all the solutions but nothing works for me.
Any suggestions on what possibly could cause the error and how do I solve it?
Error message:
[2022-01-29T10:35:40.257Z] #firebase/firestore:, Firestore (9.6.4):
Could not reach Cloud Firestore backend. Connection failed 1 times.
Most recent error: FirebaseError: [code=permission-denied]: Cloud
Firestore API has not been used in project xxxx before or it
is disabled.
If you enabled this API recently, wait a few minutes for
the action to propagate to our systems and retry. This typically
indicates that your device does not have a healthy Internet connection
at the moment. The client will operate in offline mode until it is
able to successfully connect to the backend.
Firebase config.ts file:
import firebase from 'firebase/compat/app';
import 'firebase/compat/auth';
import 'firebase/compat/firestore';
const firebaseConfig = {
apiKey: 'XXXXXXXXX',
authDomain: 'XXXXXXXXX.firebaseapp.com',
databaseURL: 'https://XXXXXXXXX.firebasedatabase.app',
projectId: 'XXXXXXXXX',
storageBucket: 'gs://XXXXXXXXX.appspot.com',
messagingSenderId: 'XXXXXXXXX',
appId: 'XXXXXXXXX',
};
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
}
export { firebase };
LoginScreen.tsx
import React, { useState } from 'react'
import { Image, Text, TextInput, TouchableOpacity, View } from 'react-native'
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import styles from './styles';
import { firebase } from '../../firebase/config';
export default function LoginScreen({navigation}) {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const onFooterLinkPress = () => {
navigation.navigate('Registration')
}
const onLoginPress = () => {
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then((response) => {
const uid = response.user.uid
const usersRef = firebase.firestore().collection('users')
usersRef
.doc(uid)
.get()
.then(firestoreDocument => {
if (!firestoreDocument.exists) {
alert("User does not exist anymore.")
return;
}
const user = firestoreDocument.data()
navigation.navigate('Home', {user})
})
.catch(error => {
alert(error)
});
})
.catch(error => {
alert(error)
})
}
return (
<View style={styles.container}>
<KeyboardAwareScrollView
style={{ flex: 1, width: '100%' }}
keyboardShouldPersistTaps="always">
<Image
style={styles.logo}
source={require('../../assets/images/logo.png')}
/>
<TextInput
style={styles.input}
placeholder='E-mail'
placeholderTextColor="#aaaaaa"
onChangeText={(text) => setEmail(text)}
value={email}
underlineColorAndroid="transparent"
autoCapitalize="none"
/>
<TextInput
style={styles.input}
placeholderTextColor="#aaaaaa"
secureTextEntry
placeholder='Password'
onChangeText={(text) => setPassword(text)}
value={password}
underlineColorAndroid="transparent"
autoCapitalize="none"
/>
<TouchableOpacity
style={styles.button}
onPress={() => onLoginPress()}>
<Text style={styles.buttonTitle}>Log in</Text>
</TouchableOpacity>
<View style={styles.footerView}>
<Text style={styles.footerText}>Don't have an account? <Text onPress={onFooterLinkPress} style={styles.footerLink}>Sign up</Text></Text>
</View>
</KeyboardAwareScrollView>
</View>
)
}
Your error message says FirebaseError: [code=permission-denied]: Cloud Firestore API has not been used in project xxxx before or it is disabled.
The app tries to read a user document after signing in, but seems to have no access.
Check your firestore rules in your firebase console to see if read rules are valid. Firestore may have initialized with rules that allow only admin users to read/write, or rules that allow all read/write only for a month after its creation.
To make your app work, your firestore rules need to allow read access on users collection. Here are the rules you can set on your firestore. The more specific rules you set, the more secure the app is.
Allow unauthenticated access on all collections
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true;
}
}
}
Allow authenticated access on all collections
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth != null;
}
}
}
Allow authenticated access on users collection
service cloud.firestore {
match /databases/{database}/documents {
match /users/{document=**} {
allow read, write: if request.auth != null;
}
}
}
See the firestore security doc for more.

Expo, React Native, Stripe: Setting up future payment methods not working

I’m in desperate need for help.
So I have a side project for an iOS app using Expo / React Native. And I'm having issues with setting up future payment methods using Stripe & Expo’s stripe library.
Our back-ender set up a graphql back-end, and provides me with all the variables I need. I’m trying to set up future payments to charge clients later, but I’m having trouble having with the paymentIntentSheet not showing up after creating an intent and fetching the clientSecret, ephemeralKey and customerId from our back-end. Now i don’t know where the issue is.. Is it because of me using the wrong versions? Maybe incorrect installation? Are the variables I’m using right..?
I used the following documentation page(s) as a guide:
https://stripe.com/docs/payments/save-and-reuse?platform=react-native
https://github.com/stripe/stripe-react-native#expo
These are the version numbers of the libraries I’m using, relevant to this topic/issue:
"expo": "~41.0.1",
"react": "16.13.1",
"react-dom": "16.13.1",
"react-native": "https://github.com/expo/react-native/archive/sdk-41.0.0.tar.gz",
"#stripe/stripe-react-native": "0.1.1"
These are the steps I took:
Install stripe-react-native, and add it to my app.json as a plugin:
"plugins": [
[
"#stripe/stripe-react-native",
{
"merchantIdentifier": "",
"enableGooglePay": false
}
]
],
On global level, I import the StripeProvider component and pass down the given publishable key:
pk_live_51[.....]
On global level it’ll look like this:
<StripeProvider
publishableKey="pk_live_51[...]"
>
<AuthProvider>
<ApolloProvider client={client}>
<InnerApp />
</ApolloProvider>
</AuthProvider>
</StripeProvider>
Then according to the stripe docs, at the component where I'll be setting up future payments, I am supposed to fetch the setupIntent, ephemeralKey, and the customer from the back-end. In this case, it's done in the useEffect of my component. I was provided with a graphql mutation to obtain these values:
mutation (
$createUserPaymentMethodSetupIntentInput: CreateUserPaymentMethodSetupIntentInput!
) {
createUserPaymentMethodSetupIntent(
input: $createUserPaymentMethodSetupIntentInput
) {
setupIntentId
clientSecret
customerId
ephemeralKeySecret
}
}
I then call the function that will eventually provide me with all the necessary variables:
createIntent({
variables: {
createUserPaymentMethodSetupIntentInput: {
userUid: userUid,
},
},
})
.then((res) => {
const clientSecret =
res.data.createUserPaymentMethodSetupIntent.clientSecret
const setupIntentId =
res.data.createUserPaymentMethodSetupIntent.setupIntentId
const ephemeralKeySecret =
res.data.createUserPaymentMethodSetupIntent.ephemeralKeySecret
const customerId =
res.data.createUserPaymentMethodSetupIntent.customerId
// IGNORE THIS FOR NOW
initializePaymentSheet(
clientSecret,
setupIntentId,
ephemeralKeySecret,
customerId
)
})
.catch((err) => console.log({ graphqlError: err }))
The function gives me the following response:
Object {
"data": Object {
"createUserPaymentMethodSetupIntent": Object {
"__typename": "CreatedUserPaymentMethodSetupIntent",
"clientSecret": "seti_1K[....]",
"customerId": "cus_[...]",
"ephemeralKeySecret": "ek_live_[...]",
"setupIntentId": "seti_[...]",
},
},
According to the docs, I should use the setupIntent, ephemeralKey, and customer values as variables in one of their given functions/hooks called “initPaymentSheet” which should initialize the paymentsheet on their end.
These functions are imported like this:
const { initPaymentSheet, presentPaymentSheet } = useStripe();
In step 3, you see that I call a function that then calls the initPaymentSheet after successfully fetching the values from the server.
initializePaymentSheet(
clientSecret,
setupIntentId,
ephemeralKeySecret,
customerId
)
The initializePaymentSheet function looks like this:
const initializePaymentSheet = (
clientSecret,
setupIntentId,
ephemeralKeySecret,
customerId
) => {
initPaymentSheet({
customerId: customerId,
customerEphemeralKeySecret: ephemeralKeySecret,
setupIntentClientSecret: setupIntentId,
})
.then((res) => {
console.log(res)
setDisabledButton(false)
})
.catch((err) => console.log("error.."))
}
As you can see, I call the initPaymentSheet hook there, exactly like shown on the docs, and pass in the values i received from the back-end. However, after doing this i get the following error in the console:
Object {
"error": Object {
"code": "Failed",
"message": "You must provide the paymentIntentClientSecret",
},
}
This didn’t seem like a huge error, so I went ahead and changed the initPaymentSheet parameters by adding the paymentIntentClientSecret field and passed in the clientSecret value which wasn’t previously used:
initPaymentSheet({
customerId: customerId,
customerEphemeralKeySecret: ephemeralKeySecret,
setupIntentClientSecret: setupIntentId,
paymentIntentClientSecret: clientSecret
})
.then((res) => {
console.log(res)
setDisabledButton(false)
})
.catch((err) => console.log("little error.."))
After calling the function and seeing the error disappear, and the console.log shown above logs the following in the console:
Object {
"paymentOption": null,
}
I didn’t think too much of this, and thought it says null just because I have no previously set paymentOptions. I was just happy there were no more errors.
In the .then chain, you see that i enable a button that basically allows a user to call a function that would present a payment sheet where users can submit their paymentMethod. This button is disabled, because I think you should initialize the paymentSheet first before enabling it?
<WideButton
disabled={disabledButton}
text="Add New Payment Method"
clicked={openPaymentSheet}
/>
Anyways, now that the button is finally enabled, the user can click on it and it'll call the following function:
const openPaymentSheet = async () => {
setDisabledButton(true)
const { error, paymentOption } = await presentPaymentSheet()
if (error) {
console.log(error)
setDisabledButton(false)
Alert.alert(`Error code: ${error.code}`, error.message)
}
if (paymentOption) {
setDisabledButton(false)
Alert.alert(
"Success",
"Your payment method is successfully set up for future payments!"
)
console.log(paymentOption)
}
}
Now to quote the stripe docs:
When your customer taps the Set up button, call presentPaymentSheet() to open the sheet. After the customer completes setting up their payment method for future use, the sheet is dismissed and the promise resolves with an optional StripeError.
So, that's exactly what I did: Call the presentPaymentSheet, but then i get the following error:
Object {
"code": "Failed",
"message": "There was an unexpected error -- try again in a few seconds",
}
Now this is where I’m stuck, because it doesn’t provide me with any more information than given above. I’ve tried looking everywhere, and some resources tell me that I should update my stripe, some say i should add stripe to my plugins in app.json. I’ve done all of that and I can’t still figure it out.
Here is a video showing you the behavior in action:
https://user-images.githubusercontent.com/29804130/146274443-82c581ba-8913-4c87-ad2e-5b8719680fed.mov
Here is the code of the entire component:
// steps
// 1. call graphql query to set up intent, retrieve the clientsecret and setupintentid
// 2. call stripes initPaymentSheet's function and pass in useruid, clientsecret and setupintentid
// 3. when initpaymentsheet is ready, enable button for user to add payment information
// 4. Retrieve the payment information and call the createpaymentmethod mutation
// 5. disable button again, and refresh page
export default function PaymentMethods({ userUid }) {
const { initPaymentSheet, presentPaymentSheet } = useStripe()
const [disabledButton, setDisabledButton] = useState(false)
const [createIntent, { data, loading, error }] = useMutation(
ADD_PAYMENT_METHOD_INTENT
)
useEffect(() => {
createUserPaymentMethodIntent()
}, [])
const createUserPaymentMethodIntent = () => {
setDisabledButton(true)
createIntent({
variables: {
createUserPaymentMethodSetupIntentInput: {
userUid: userUid,
},
},
})
.then((res) => {
console.log(res)
const clientSecret =
res.data.createUserPaymentMethodSetupIntent.clientSecret
const setupIntentId =
res.data.createUserPaymentMethodSetupIntent.setupIntentId
const ephemeralKeySecret =
res.data.createUserPaymentMethodSetupIntent.ephemeralKeySecret
const customerId =
res.data.createUserPaymentMethodSetupIntent.customerId
initializePaymentSheet(
clientSecret,
setupIntentId,
ephemeralKeySecret,
customerId
)
})
.catch((err) => console.log({ graphqlError: err }))
}
const initializePaymentSheet = (
clientSecret,
setupIntentId,
ephemeralKeySecret,
customerId
) => {
initPaymentSheet({
customerId: customerId,
customerEphemeralKeySecret: ephemeralKeySecret,
setupIntentClientSecret: setupIntentId,
paymentIntentClientSecret: clientSecret,
})
.then((res) => {
console.log(res)
setDisabledButton(false)
})
.catch((err) => console.log("little error.."))
}
const openPaymentSheet = async () => {
setDisabledButton(true)
const { error } = await presentPaymentSheet()
if (error) {
Alert.alert(`Error code: ${error.code}`, error.message)
} else {
Alert.alert(
"Success",
"Your payment method is successfully set up for future payments!"
)
}
}
return (
<ScrollView>
<PaymentMethodList userUid={userUid} />
<WideButton
disabled={disabledButton}
text="Add New Payment Method"
clicked={openPaymentSheet}
/>
</ScrollView>
)
}
someone plz help :(
you might want to check the logs in your Stripe Dashboard (Dashboard -> Developers -> Logs). From there you'll be able to see more info about this error,

react native - `Geolocation.getCurrentPosition` returns "No location provider available"

I'm trying to use this library: #react-native-community/geolocation
const requestLocationPermission = async (success, failure) => {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION
)
if( granted === PermissionsAndroid.RESULTS.GRANTED ) {
console.log("You can use the location")
Geolocation.getCurrentPosition(
({ coords }) => {
console.log("coords: ", coords)
},
(errObject) => {
console.log("message: ", errObject) // it gets thrown here
}
)
} else {
console.log("You cannot use the location")
}
} catch (err) {
}
}
It prompts user to either allow or deny the app access to their location. After allowing I get this:
{"PERMISSION_DENIED": 1, "POSITION_UNAVAILABLE": 2, "TIMEOUT": 3, "code": 2, "message": "No location provider available."}
In AndroidMenufest.xml:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Nothing need more just do like this useState(0) for both lat and lang don't provide any string write this Geolocation.getCurrentPosition((data)=>(console.log(data)
//here you can set the states
))

How to upload local image file on React Native app to Rails api?

I'm having trouble understanding how a local file path from a smartphone could possibly get uploaded on the server side with a Rails api for instance.
The file path that we're sending to the backend doesn't mean anything to the server?
I'm getting a uri from the response like this:
file:///Users/.../Documents/images/5249F841-388B-478D-A0CB-2E1BF5511DA5.jpg):
I have tried to send something like this to the server:
let apiUrl = 'https://vnjldf.ngrok.io/api/update_photo'
let uriParts = uri.split('.');
let fileType = uri[uri.length - 1];
let formData = new FormData();
formData.append('photo', {
uri,
name: `photo.${fileType}`,
type: `image/${fileType}`,
});
let options = {
method: 'POST',
body: formData,
headers: {
Accept: 'application/json',
'Content-Type': 'multipart/form-data',
},
};
But I'm unsure what it is and how to decript it on the backend.
I have also tried sending the uri direclty but of course I'm getting the following error:
Errno::ENOENT (No such file or directory # rb_sysopen -...
Any help/guidance would be much appreciated.
I have recently spent 1+ hour debugging something similar.
I found out that if you make a POST to your Rails backend from your React Native app using this json:
let formData = new FormData();
formData.append('photo', {
uri,
name: `photo.${fileName}`,
type: `image/${fileType}`,
});
Rails will automatically give you a ActionDispatch::Http::UploadedFile in your params[:photo], which you can attach directly to your model like Photo.create(photo: params[:photo]) and it simply works.
However, if you don't pass a filename, everything breaks and you'll get a huge string instead and it will raise a ArgumentError (invalid byte sequence in UTF-8).
So, based on your code, I can spot the bug right on: you are passing name as photo.${fileType}, which is wrong, and should be photo.${fileName} (update accordingly to get your image filename ... console.log(photo) in your React Native code will show you the correct one.
Maintain issues with deleting and adding new files
This is how I managed to do it add multiple file upload and maintain issues with deleting and adding new files
class User < ApplicationRecord
attribute :photos_urls # define it as an attribute so that seriallizer grabs it to generate JSON i.e. as_json method
has_many_attached :photos
def photos_urls
photos.map do |ip|
{url: Rails.application.routes.url_helpers.url_for(ip), signed_id: ip.signed_id}
end
end
See about signed_id here. It describes how you can handle multiple file upload.
Controller looks like
def update
user = User.find(params[:id])
if user.update(user_params)
render json: {
user: user.as_json(except: [:otp, :otp_expiry])
}, status: :ok
else
render json: { error: user.errors.full_messages.join(',') }, status: :bad_request
end
end
...
private
def user_params
params.permit(
:id, :name, :email, :username, :country, :address, :dob, :gender,
photos: []
)
end
React Native part
I am using react-native-image-crop-picker
import ImagePicker from 'react-native-image-crop-picker';
...
const photoHandler = index => {
ImagePicker.openPicker({
width: 300,
height: 400,
multiple: true,
}).then(selImages => {
if (selImages && selImages.length == 1) {
// Make sure, changes apply to that image-placeholder only which receives 'onPress' event
// Using 'index' to determine that
let output = images.slice();
output[index] = {
url: selImages[0].path, // For <Image> component's 'source' field
uri: selImages[0].path, // for FormData to upload
type: selImages[0].mime,
name: selImages[0].filename,
};
setImages(output);
} else {
setImages(
selImages.map(image => ({
url: image.path, // For <Image> component's 'source' field
uri: image.path, // for FormData to upload
type: image.mime,
name: image.filename,
})),
);
}
});
};
...
<View style={style.imageGroup}>
{images.map((item, index) => (
<TouchableOpacity
key={`img-${index}`}
style={style.imageWrapper}
onPress={() => photoHandler(index)}>
<Image style={style.tileImage} source={item} />
</TouchableOpacity>
))}
</View>
Uploader looks like
// ../models/api/index.js
// Update User
export const updateUser = async ({ id, data }) => {
// See https://developer.mozilla.org/en-US/docs/Web/API/FormData/append
let formData = new FormData(data);
for (let key in data) {
if (Array.isArray(data[key])) {
// If it happens to be an Image field with multiple support
for (let image in data[key]) {
if (data[key][image]?.signed_id) {
// if the data has not change and it is as it was downloaded from server then
// it means you do not need to delete it
// For perverving it in DB you need to send `signed_id`
formData.append(`${key}[]`, data[key][image].signed_id);
} else if (data[key][image]?.uri && data[key][image]?.url) {
// if the data has change and it is as it has been replaced because user selected a different image in place
// it means you need to delete it and replace it with new one
// For deleting it in DB you should not send `signed_id`
formData.append(`${key}[]`, data[key][image]);
}
}
} else {
formData.append(key, data[key]);
}
}
return axios.patch(BASE_URL + "/users/" + data.id, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
});
};
and Saga worker looks like
import * as Api from "../models/api";
// worker Saga:
function* updateUserSaga({ payload }) {
console.log('updateUserSaga: payload', payload);
try {
const response = yield call(Api.updateUser, {
id: payload.id,
data: payload,
});
if (response.status == 200) {
yield put(userActions.updateUserSuccess(response.data));
RootNavigation.navigate('HomeScreen');
} else {
yield put(userActions.updateUserFailure({ error: response.data.error }));
}
} catch (e) {
console.error('Error: ', e);
yield put(
userActions.updateUserFailure({
error: "Network Error: Could not send OTP, Please try again.",
})
);
}
}

Resources