I have this post screen where I can pick an image from the camera roll and type a text and I want it to be saved in Firebase.
Here is my code in fire.js
addPost = async({text,localUri}) => {
const remoteUri = await this.uploadPhotoAsync(localUri)
return new Promise((res,rej) => {
this.firestore.collection("posts").add({
text,
uid: this.uid,
timestamp:this.timestamp,
image: remoteUri
})
.then(ref => {
res(ref)
})
.catch(error => {
rej(error)
})
})
}
uploadPhotoAsync = async uri => {
const path = `photos/${this.uid}/${Date.now()}.jpg`
return new Promise(async (res,rej) => {
const response = await fetch(uri)
const file = await response.blob()
let upload = firebase.storage().ref(path).put(file)
upload.on(firebase.storage.TaskEvent.STATE_CHANGED,snapshot => {},
err => {
rej(err)
},
async () => {
const url = await upload.snapshot.ref.getDownloadURL()
res(url)
}
)
})
}
And here is my postscreen.js screen where I'm getting the error can't find variable atob,
please suggest me a solution.
handlePost = () => {
Fire.shared.addPost({text:this.state.text.trim(),
localUri:this.state.image })
.then(ref => {
this.setState({text:"",image:undefined})
this.props.navigation.goBack()
}).catch(error => {
alert(error)
})
}
pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing:true,
aspect:[4,3]
})
if(!result.cancelled) {
this.setState({image: result.uri})
}
}
By the way, I can see the image is saved in Firestore storage but I can't see the text and photo in the Firestore database
This is a bug in some versions of firebase.
A workaround is to import base64 in the app.js and define it in case it's not defined.
import {decode, encode} from 'base-64'
if (!global.btoa) { global.btoa = encode }
if (!global.atob) { global.atob = decode }
Related
how can I run a function in the whole app? like if that function is triggered then even if a user is navigating to a different screen then also that function will do its work until that function work is done how can I do this in react native? I want to do this with my PostImageHandler once postimagehandler is triggered then that function will run and it won't stop until the image is uploaded also user can navigate through different screens during that process
const [loading1, setLoading1] = useState(false)
const [loading2, setLoading2] = useState(false)
const [photo, setPhoto] = useState(null)
const { postImage } = route.params;
const PostImageHandler = useCallback(async () => {
if (!postImage.cancelled) {
const response = await fetch(postImage);
const blob = await response.blob();
const filename = postImage.substring(postImage.lastIndexOf('/') + 1);
const ref = firebase.storage().ref().child(filename);
const snapshot = await ref.put(blob);
const url = await snapshot.ref.getDownloadURL();
setPhoto(url)
console.log(url)
}
}, [postImage])
useEffect(() => {
PostImageHandler();
}, [PostImageHandler])
const handleUpload = useCallback(() => {
if (postImage != null) {
AsyncStorage.getItem('user')
.then(data => {
setLoading2(true)
fetch('https://mybackend.com/addpost', {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: JSON.parse(data).user.email,
post: postImage,
})
})
.then(res => res.json())
.then(data => {
if (data.message == 'Photo added successfully') {
alert('Photo added successfully')
setLoading2(false)
navigation.navigate('home')
} else {
alert('Something went wrong, please try again')
setLoading2(false)
}
})
})
} else {
alert('Please wait photo is processing')
}
}, [photo, navigation, setLoading2]);
I'm doing an API using React, SQL and Sequelize. I'm already finished but now I was asked to do my API request using JavaScript oriented object. The problem is, I don't really know how to do it, and I don't really understand my errors.
This is the class I am trying to do:
API class
class Api {
constructor(hostName, token) {
this.hostName = hostName
this.token = token
}
async getPost() {
return await fetch('api/post/')
.then(res => {
if (!res.ok) {
throw Error(res.statusText + "-" + res.url);
}
return res;
})
.then(post => {
console.log(post)
return post
})
.catch(err => {
console.log("Page non trouvée")
console.log(err)
})
}
}
export const apiRequest = new Api();
Axios config
import axios from 'axios';
class AxiosConfig {
constructor() {
this.axios = axios.create();
this.axios.defaults.baseURL = `${process.env.REACT_APP_API_URL}`;
this.axios.defaults.headers = {
'Content-Type': 'application/json',
};
//All request will wait 2 seconds before timeout
this.axios.defaults.timeout = 2000;
this.axios.defaults.withCredentials = true;
}
GET = async (url) => {
return await this.axios.get(`/${url}`);
}
POST = async (url, payload) => {
return await this.axios.post(`/${url}`, payload);
}
PUT = async (url, payload) => {
return await this.axios.put(`/${url}`, payload);
}
DELETE = async (url) => {
return await this.axios.delete(`/${url}`);
}
}
export const axiosInstance = new AxiosConfig();
HandlePost() is the function I'm tring to put in oriented object
NewPostForm
import React, { useContext, useEffect, useState } from 'react';
import { NavLink } from 'react-router-dom';
import { UserContext } from '../../UserContext';
import { apiRequest } from '../../utils/api';
import { axiosInstance } from '../../utils/AxiosConfig'
import landscape from './../../assets/icons/landscape.svg'
const NewPostForm = () => {
const uid = useContext(UserContext)
const [userPicture, setUserPicture] = useState('')
const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('')
const [message, setMessage] = useState('')
const [postPicture, setPostPicture] = useState('')
const [file, setFile] = useState('')
useEffect(() => {
const getUserInfo = async () => {
if (uid !== null) {
const userId = uid.userId
await axiosInstance.GET (`api/auth/${userId}`)
.then((res) => {
setFirstName(res.data.firstName)
setLastName(res.data.lastName)
setUserPicture(res.data.profile)
})
.catch((err) => {
console.log(err)
})
}
}
getUserInfo()
}, [uid, firstName, lastName])
console.log('parfait', axiosInstance.GET(`api/post`)) // old one, works perfectly
console.log('objectif', apiRequest.getPost()) // new one, not getting the object I need
// console.log(axiosInstance.GET(apiRequest.getPost`${uid.userId}`)) // error
const handlePost = async () => {
if (message || postPicture) {
const data = new FormData();
data.append("UserId", uid.userId);
data.append("content", message);
if (file) {
data.append("image", file);
}
try {
const res = await axiosInstance.POST(`api/post`, data);
console.log('File uploaded', res.data);
// window.location = '/'
} catch (err) {
console.error('Failed to upload file', err);
}
cancelPost()
} else {
alert("Veuillez entrer un message")
}
}
const handlePicture = (e) => {
setPostPicture(URL.createObjectURL(e.target.files[0]))
setFile(e.target.files[0])
}
const cancelPost = () => {
setMessage('')
setPostPicture('')
setFile('')
}
return (
<form className='post-container' >
<h2 className='h1'>Créer un post</h2>
<NavLink to="/profile">
<figure title='Profil utilisateur' className='new card-header'>
<img className='nav-profile' src={userPicture ? userPicture : "./images/img/profile.png"} width='50px' alt="profil de l'utilisateur" />
<h3 className='h2'>{firstName} {lastName}</h3>
</figure>
</NavLink>
<div className='post-form'>
<textarea
type="text"
name="message"
id="message"
cols="50"
rows="5"
placeholder="Quoi de neuf ?"
onChange={(e) => setMessage(e.target.value)}
value={message}
></textarea>
{postPicture && <img src={postPicture} alt="preview" className="img-preview" />}
</div>
<div className='footer-form'>
<div className='icon'>
<input
type="file"
id='file-upload'
name='file'
accept='.jpg, .jpeg, .png'
onChange={(e) => handlePicture(e)}
/>
<label className='file-input__label' htmlFor="file-upload">
<img className='svg' src={landscape} alt="upload icone paysage" />
Ajouter une l'image
</label>
</div>
<div className='new button-container'>
{message || postPicture ? (
<button className='new cancel-btn' onClick={(e) => cancelPost()}>Annuler</button>
) : null}
<button className='new validate-btn' onClick={(e) => handlePost()}>Envoyer</button>
</div>
</div>
</form>
);
};
export default NewPostForm;
My first console.log() returning the object that I need, when the second (supposed oriented object one) doesn't. I don't know what I'm doing wrong and the documentation doesn't help, I don't have the "good" questions.
Thanks
EDIT :
class Api {
constructor(hostName, token) {
this.hostName = hostName
this.token = token
}
async getPost() {
try {
const res = await fetch('api/post')
if (!res.ok) {
throw Error(res.statusText + "-" + res.url);
}
return await res.json();
} catch(err) {
console.log("Page non trouvée")
console.log(err)
}
}
}
export const apiRequest = new Api();
The problem is with the code that how you used fetch method. This is from the documentation.
The Response object, in turn, does not directly contain the actual JSON response body but is instead a representation of the entire HTTP response. So, to extract the JSON body content from the Response object, we use the json() method, which returns a second promise that resolves with the result of parsing the response body text as JSON.
You have to update your getPost method like below.
async getPost() {
return await fetch('api/post/').then(res => {
if (!res.ok) {
throw Error(res.statusText + "-" + res.url);
}
return res.json();
}).then(post => {
console.log(post)
return post
})
.catch(err => {
console.log("Page non trouvée")
console.log(err)
})
}
Check the documentation
You already using async and await so you don't need to use .then. The code can be updated like below.
async getPost() {
try {
const res = await fetch('api/post/')
if (!res.ok) {
throw Error(res.statusText + "-" + res.url);
}
return await res.json();
} catch(err) {
console.log("Page non trouvée")
console.log(err)
}
}
I've been building an app with Firebase & React Native primarily using Firestore. I started to use Firestore and its been great, but for some reason when writing to Firestore, it is only working on the first attempt (when i remove the app, rebuild, and perform my write).
I tried to do the exact same thing except write to Firestore and everything works as expected.
I am also receiving no error!
Here is what I am doing:
export const addBrandProduct = (postObj) => {
return () => {
firebase
.firestore()
.collection('brandProducts')
.add(postObj)
.then((docRef) => {
console.log("Document written with ID: ", docRef.id);
Actions.categories();
})
.catch(error => {
console.error("Error adding document: ", error);
});
};
};
For more of a reference, here is my component code that calls addBrandProduct()
onUploadImages = () => {
let photo =
Platform.OS === 'ios'
? this.state.images.map(img => img.uri.replace('file://', ''))
: this.state.images.map(img => img.uri);
photo.forEach((image, i) => {
const sessionId = new Date().getTime();
const Blob = RNFetchBlob.polyfill.Blob;
const fs = RNFetchBlob.fs;
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
window.Blob = Blob;
let uploadBlob = null;
let mime = 'image/jpg';
const imageRef = firebase
.storage()
.ref('brandProducts/')
.child(`${this.props.userData.uid}`)
.child(`${sessionId}-${i}`);
fs.readFile(image, 'base64')
.then(data => {
return Blob.build(data, {type: `${mime};BASE64`});
})
.then(blob => {
uploadBlob = blob;
return imageRef.put(blob, {contentType: mime});
})
.then(() => {
uploadBlob.close();
return imageRef.getDownloadURL();
})
.then(url => {
//if this is the last uploaded image, post data to db
if (i === this.state.images.length - 1) {
const urls = {
...this.state.urls,
[i]: url,
};
const postObj = {
...this.state.postObj,
urls: urls,
};
this.props.addBrandProduct(postObj);
} else {
this.setState({
urls: {
...this.state.urls,
[i]: url,
},
});
}
})
.catch(error => {
console.log(error);
});
});
};
Basically, I am uploading a maximum of 3 images along with some data for it. In order to ensure I am uploading them all prior to adding the post data (writing to firestore) I am using a forEach and on the last upload, when it completes, I am calling the action to write the post data.
Edition
Hum addBrandProduct is a function that create another function.
So when you call this.props.addBrandProduct(postObj) nothing is sent to firestore, you just create a new function that should be called.
Maybe you can go out this stuff and call firebase directly, ensuring that everything works and then go back to the redux way if you still want to use it. I also make it parallelized instead of sequentials. Hope it help, hard to find the real problem when it can come from anywhere.
onUploadImages = () => {
let photo = Platform.OS === 'ios'
? this.state.images.map(img => img.uri.replace('file://', ''))
: this.state.images.map(img => img.uri);
Promise.all( photo.map( image => {
const sessionId = new Date().getTime();
const Blob = RNFetchBlob.polyfill.Blob;
//This is kind useless
//const fs = RNFetchBlob.fs;
//This is not used
//window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
//This is not adviced
//window.Blob = Blob;
let uploadBlob = null;
let mime = 'image/jpg';
const imageRef = firebase
.storage()
.ref('brandProducts/')
.child(`${this.props.userData.uid}`)
.child(`${sessionId}-${i}`);
return fs.readFile(image, 'base64')
.then(data => {
return RNFetchBlob.polyfill.Blob.build(data, {type: `${mime};BASE64`});
})
.then(blob => {
uploadBlob = blob;
return imageRef.put(blob, {contentType: mime});
})
.then(() => {
uploadBlob.close();
return imageRef.getDownloadURL();
});
))
.then( results => {
//results is, here, [ urlFromFirst, urlFronSecond, ...]
const urls = { ...this.state.urls};
results.forEach( (r, i) => urls[i] = r );
const postObj = {
...this.state.postObj,
urls
};
return firebase
.firestore()
.collection('brandProducts')
.add(postObj)
})
.then( docRef => {
console.log("Document written with ID: ", docRef.id);
})
.catch(error => {
console.error(error);
});
};
I tried to download the image which is in firebase storage which link is store in database. When I tried to download the image, it takes more time to execute while for loop is completed.
Is there any process that somehow I download in time which doesn't make the function really slow? I already solve this issue using setTimeout but I hope there may be a better solution than mine. Help me! thank you!
export const shampooHandler = () => {
return (dispatch) => {
dispatch(shampooStart());
const data = [];
const imgList = [];
fire
.database()
.ref()
.child("Shampoo")
.once("value")
.then((response) => {
for (let i = 0; i < response.val().length; i++) {
fire.storage().refFromURL(response.val()[i].img).getDownloadURL().then((image) => {
imgList.push(image);
})
.catch((error) => {
dispatch(shampooError(error));
});
setTimeout(() => {
name = response.val()[i].name;
description = response.val()[i].description;
value = response.val()[i].value;
img = imgList[i];
data.push({ name, description, value, img });
if (i === (response.val().length - 1)) {
dispatch(shampooSuccess(data));
}
}, 3000);
}
})
.catch((error) => {
dispatch(shampooError(error));
});
};
};
I spend a day finding a right solution for it. It may help someone to find solution in future. Thanks guys for giving a thought and specially DougStevensen to tiggering me an idea
export const shampooHandler = () => {
return (dispatch) => {
dispatch(shampooStart());
const data = [];
const imglist = [];
fire.database().ref().child("Shampoo").once("value").then((response) => {
response.val().forEach(element => {
const promise = imageUrlHandler(element.img).then(url => {
return url;
}).catch(error =>{
dispatch(shampooError(error));
})
imglist.push(promise);
//all the promise call to download the images
Promise.all(imglist).then(items =>{
const dataCollection = {
name: element.name,
description: element.description,
value: element.value,
img: items[items.length - 1]
}
data.push(dataCollection);
if(data.length === response.val().length){
dispatch(shampooSuccess(data));
}
}).catch(err =>dispatch(shampooError(err)));
})
}).catch(error => {
dispatch(shampooError(error));
})
}
}
export const imageUrlHandler = (databaseUrl) => {
return new Promise((resolve,reject)=> {
fire.storage().refFromURL(databaseUrl).getDownloadURL().then((url) => {
resolve(url);
})
.catch((error) => {
reject(error)
});
})
}
I want to save a bunch of Images to Firebase storage and it's saved very well "as known image by image " in Firebase Storage, so after I saved it I want to get all the Uri and put it into Real-time DB as an Array-object like this
but I'm tried here in this code and also save one image just like this!
So how to handle these to Get all the images in the Storage and then put them into an array in DB?
// Open Gallery
pickMultiple = () => {
ImagePicker.openPicker({
multiple: true
})
.then(images => {
this.setState({
images: images.map(i => {
return {
uri: i.path,
width: i.width,
height: i.height,
mime: i.mime
};
})
});
})
.catch(e => console.log(e));
};
_SaveImagesToFirebase = () => {
const uid = firebase.auth().currentUser.uid; // Provider
const { images } = this.state;
const provider = firebase.database().ref(`providers/${uid}`);
images.map(image => {
let file = image.uri;
const path = "Img_" + Math.floor(Math.random() * 1500 + ".jpg");
const ref = firebase
.storage()
.ref(`provider/${uid}/ProviderGalary/${path}`);
let imagesArray = [];
ref
.put(file)
.then(() => {
ref
.getDownloadURL()
.then(
images => {
console.log(images);
imagesArray.push({
uri: images
});
},
error => console.log(error)
)
.then(() => {
provider
.update({
Images: imagesArray
})
.then(() => console.log("done with imgs"));
});
console.log("#inside", imagesArray);
})
.then(() => {
setTimeout(() => {
this.props.navigation.navigate("Home");
}, 2000);
});
console.log("#OUT", imagesArray);
});
};
UH My bad, I just define imagesArray inside map() it should be outside! like this,
_SaveImagesToFirebase = () => {
const uid = firebase.auth().currentUser.uid; // Provider
const { images } = this.state;
const provider = firebase.database().ref(`providers/${uid}`);
=> let imagesArray = [];
images.map(image => {
let file = image.uri;
const path = "Img_" + Math.floor(Math.random() * 1500 + ".jpg");
const ref = firebase
.storage()
.ref(`provider/${uid}/ProviderGalary/${path}`);
ref
.put(file)
.then(() => {
ref
.getDownloadURL()
.then(
images => {
console.log(images);
imagesArray.push({
uri: images
});
},
error => console.log(error)
)
.then(() => {
provider
.update({
Images: imagesArray
})
.then(() => console.log("done with imgs"));
});
})
.then(() => {
setTimeout(() => {
this.props.navigation.navigate("Home");
}, 2000);
});
});
};