How can I Update Document in Collection firebase - reactjs

So I have this Avatar and I want to display the Avatar not only on the Profile but also on the Feed and some other pages. In my "User" collection, I have an object called "photoURL". What I’m trying to do is that when someone is adding or changing the Profile picture, the "photoURL" should update automatically in the collection.
My logic behind this is that I need this to display the photo on other pages and to do that I have to save it to the collection so I can access it.
Maybe my logic is wrong, I don't know, but I didn't find another way and I think this is the easiest.
This is the user collection code:
function onSignUp() {
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then((result) => {
firebase
.firestore()
.collection("users")
.doc(firebase.auth().currentUser.uid)
.set({
name,
email,
role: isTeacher ? "pädagoge" : "parent",
photoURL: null,
});
console.log(result);
})
.catch((error) => {
console.log(error);
});
The Avatar Chooser Code :
async function handleFileInputChange(e) {
const files = e.target.files;
const file = files[0];
const storage = firebase.storage();
const usersImageRef = storage
.ref()
.child(`users/${user.uid}/profilepicture.jpg`);
const snap = await usersImageRef.put(file);
const donwloadURL = await snap.ref.getDownloadURL();
setDownloadURL(donwloadURL);
// Save photoUrl in user collection
// -> {name : "...", email : "...", role : "...", photoUrl : "..."}
await firebase.auth().currentUser.updateProfile({ photoURL: donwloadURL });
setProfilePictureUrl(donwloadURL);
}
I saw this example on firbase doc:
db.collection("users").doc("frank").update({
favorites: {
food: "Ice Cream"
}
}).then(function() {
console.log("Frank food updated");
});
But I would need the code to do it dynamically, and I'm not sure how.

you can add a .then() to your handleFileInputChange() function like this:
await firebase.auth().currentUser.updateProfile({ photoURL: downloadURL }).then(()=>{
db.collection("users").doc("frank").update({
userProfileImage: downloadURL
}).catch((err)=>{
//handle errors
});
}).catch((err)=>{
//handle errors
});
do take note of spelling of downloadURL too.

firebase
.firestore()
.collection("users")
.doc(firebase.auth().currentUser.uid)
.update({
name:name,
email:email,
role: isTeacher ? "pädagoge" : "parent",
photoURL: photourl,
});

Related

When sign in with google firebase always register user as new

Tech: Firebase, Next.js, Google Sign in, Firebase Stripe exstension
Bug reproduction:
When login with Google
Subscribe on stripe
Stripe saves subscription data for that user in firestore
Logout
Login in with Google and old data are overide with new one, and Subscription is lost
Does anyone had similar problem?
Maybe my implementation of Sign-in is bad, here is the Google Sign in code:
const handleGoogleLogin = () => {
signInWithPopup(auth, googleProvider)
.then(async result => {
if (!result.user) return;
const { displayName, email, uid, providerData, photoURL, phoneNumber } =
result.user;
const name = splitName(displayName as string);
const providerId =
(providerData.length && providerData[0]?.providerId) || '';
const data = {
firstName: name?.firstName || '',
lastName: name?.lastName || '',
email,
photoURL,
phoneNumber,
providerId,
};
await updateUser(uid, data);
})
.catch(error => {
console.log('Google login error: ', error);
});
};
Update user function:
export const updateUser = async (uid: string, data: UpdateUserParams) => {
try {
if (!uid) {
return;
}
await setDoc(doc(firestore, 'users', uid), {
account: {
...data,
initials: `${data.firstName[0]}${data.lastName[0]}`,
},
});
} catch (error) {
console.error('Error updating user: ', error);
}
};
setDoc is overwriting the contents of the document with each sign-in. You should instead use set with merge to prevent overwriting the fields you don't want to lose, or check first if the document exists before creating it.
See also:
https://firebase.google.com/docs/firestore/manage-data/add-data#set_a_document
Difference Between Firestore Set with {merge: true} and Update

users are not stored in the firestore database but are visible in authentication

I am trying to build a chat application with React and firebase. As soon as the user registers, the authentication works perfectly as well as their pictures get stored in the storage bucket but the user is not stored in the firestore database under the users collection.
I am getting this error in the console:
Below is a code snippet from my register component:
try {
const res = await createUserWithEmailAndPassword(auth, email, password);
const storageRef = ref(storage, displayName);
console.log(storageRef);
const uploadTask = uploadBytesResumable(storageRef, file);
console.log(uploadTask);
uploadTask.on(
(error) => {
setErr(true);
console.log(err);
},
() => {
getDownloadURL(uploadTask.snapshot.ref).then(async (downloadURL) => {
await updateProfile(res.user, {
displayName,
photoURL: downloadURL,
});
await setDoc(doc(db, "users", res.user.uid), {
uid: res.user.uid,
displayName,
email,
photoURL: downloadURL,
});
//Document containing the user's chats
await setDoc(doc(db, "userChats", res.user.uid), {});
navigate("/");
});
}
);
} catch {
setErr(true);
}
Mixing Promise-based APIs and Callback-based APIs will lead to trouble. By exclusively sticking to one type, you can ensure that behaviour will be as you expect.
The UploadTask class is "Thenable" - it has a then() and catch() method which will be fed the upload complete and upload error events. This means you can use it with await or like any other Promise API.
Because you are doing further Promise-based work in the upload complete event, using UploadTask.on should be avoided and only be used for updating progress of the upload. As your code doesn't use this progress update feature, I'll be omitting it, but it would be done using something like:
uploadTask.on('state_changed', (snapshot) => /* progress update */);
Additionally, close to the end of your code you attempt to add an empty document to Firestore for the user's messages. This will do nothing as empty documents are automatically deleted by Firestore on the backend. As such, it will be omitted from the code below.
By applying these changes, your code becomes:
try {
// ⬇ you can unwrap user from the userCredential object, better than using "res"
const { user } = await createUserWithEmailAndPassword(auth, email, password);
const storageRef = ref(storage, displayName);
console.log(storageRef);
const uploadTask = uploadBytesResumable(storageRef, file);
console.log(uploadTask);
// if using it, add progress updater here
// uploadTask.on('state_changed', (snapshot) => /* progress update */);
const uploadSnapshot = await uploadTask; // waits for upload completion
const downloadURL = await getDownloadURL(uploadSnapshot.ref);
await updateProfile(user, {
displayName,
photoURL: downloadURL,
});
await setDoc(doc(db, "users", user.uid), {
uid: user.uid,
displayName,
email,
photoURL: downloadURL,
});
navigate("/");
} catch {
console.log(err); // don't forget to log the error so you can investigate
setErr(true);
}
Ideally, you would move most of this logic outside of your component into your UserContext using something similar to:
​const userCtx = useContext(YourAuthUserContext);
userCtx.createUserAndUserData({ email, password, displayName, file })
.then((user) => {
navigate("/");
})
.catch((err) => {
console.log("createUserAndUserData failed:", err);
setErr(true);
});

How to make a field of a document a primary key in firebase

So I want to prevent a document with same email field to get added into the firestore database.This is automatic in authentication but not in firestore.
This is my base function:
const register = async () => {
await addDoc(userCollectionRef, {username: userName, email: newUser, password: registerPassword})
try {
const user = await createUserWithEmailAndPassword(auth, newUser, registerPassword);
console.log(user);
}
catch(e) {
console.log(e.message);
console.log(e.code);
if((e.code) === "auth/email-already-in-use"){
window.alert("Email already registered");
}
}
}
What should i do to make email field a primary key?
Firestore doesn't have the concept of a primary key. The closest equivalent is the document ID.
If you want to specify your own document ID, use setDoc with a document reference instead of addDoc:
const userDocRef = doc(userCollectionRef, newUser)
await setDoc(docRef, {username: userName, email: newUser, password: registerPassword})
Also see the Firebase documentation on writing a document.
You should write in firebase after creating a user with email and password.
const register = async () => {
try {
const user = await createUserWithEmailAndPassword(auth, newUser, registerPassword).then(() => await addDoc(userCollectionRef, {username: userName, email: newUser, password: registerPassword})) ;
console.log(user);
}
catch(e) {
console.log(e.message);
console.log(e.code);
if((e.code) === "auth/email-already-in-use"){
window.alert("Email already registered");
}
}
}
As Frank mentioned above, there is no way, to achieve your purpose, you can make use of the Firebase createUser function in the sdk. If the user is already created, it will throw an error, and your write document to firebase will not be triggered.

how to upload image to storage and get the download link and store in Firestore and react native

const handleSubmit =async (values)=>{
try{
const imageUri =values.images[0];
const filename =imageUri.substring(imageUri.lastIndexOf('/')+1);
const response = await fetch(values.images[0]);
const blob = await response.blob();
var imageURLs="";
firebase.storage().ref().child("images/"+filename).put(blob)
.on('state_changed' , (snapshot) => {
},
(error) => console.log(error)
);
}catch(e){
console.log("error",e);
}
}
in this function, the image was successfully added to the storage. I need to get the image download URL to store it in the listings collection
firebase.firestore().collection('listings')
.add({
id:uuid.v4(),
title: values.title,
price: values.price,
description: values.description,
category: values.category['label'],
image:imageURLs, //this is download url of the image
createdAt: new Date(),
supplierId:user.uid
})
If you don't need to watch the progress of the upload you can just use then to get the downloadURL when the upload is finished:
firebase
.storage()
.ref()
.child("images/" + filename)
.put(blob)
.then((snapshot) => {
snapshot.ref.getDownloadURL().then((url) => {
console.log("downloadURL", url);
});
});

Can't set user data after firebase auuthentication

I'm working on a react app on firebase. The issue that fire store doesn't set up userdata after fire authentication although it's been successfully authenticated on Google. Please help me if you have some clues to solve that. And I can't get error texts here.
login = () => {
const provider = new firebase.auth.GoogleAuthProvider();
firebase
.auth()
.signInWithRedirect(provider)
.then(result => {
const user = result.user;
const userRef = firebase
.firestore()
.collection("users")
.doc(user.uid);
userRef.get().then(function(doc) {
if (doc.exists) {
console.log("User data:", doc.data());
} else {
// doc.data() will be undefined in this case
userRef.set({
uid: user.uid,
name: user.displayName,
photoURL: user.photoURL
});
}
});
});
this.props.history.push("/");
};
Additional
'user' is not defined.
'userRef' is not defined
I have tried the code. But user and userRef could not be defined here:
if (doc.exists) {
console.log("Doc for user " + user.uid + " already exists");
throw new Error("Doc for user " + user.uid + " already exists");
} else {
return userRef.set({
uid: user.uid,
name: user.displayName,
photoURL: user.photoURL
});
}
Additional 2
I don't know why but that would not work then on Firebase Authentication.
login = () => {
let user;
const provider = new firebase.auth.GoogleAuthProvider();
firebase
.auth()
.signInWithRedirect(provider)
.then(result => {
console.log("result", result);
// result has been skipped
Actually, the signInWithRedirect() method returns a promise with no return value (i.e. Promise<void>). Therefore doing
firebase
.auth()
.signInWithRedirect(provider)
.then(result => {...});
will not work.
You need to use the getRedirectResult() method, as explained in the doc:
Authenticates a Firebase client using a full-page redirect flow. To
handle the results and errors for this operation, refer to
firebase.auth.Auth.getRedirectResult.
Therefore, in your login function, you should just have two lines:
login = () => {
const provider = new firebase.auth.GoogleAuthProvider();
firebase
.auth()
.signInWithRedirect(provider);
}
And somewhere else in your code (I don't know exactly where as I don't know reactjs...) you need to have the following code (note how the different Promises are chained):
let user;
firebase
.auth()
.getRedirectResult()
.then(result => {
user = result.user;
const userRef = firebase
.firestore()
.collection('users')
.doc(user.uid);
return userRef.get();
})
.then(doc => {
if (doc.exists) {
console.log('Doc for user ' + user.uid + ' already exists');
throw new Error('Doc for user ' + user.uid + ' already exists');
} else {
return doc.ref.set({
uid: user.uid,
name: user.displayName,
photoURL: user.photoURL
});
}
})
.then(() => {
this.props.history.push('/');
})
.catch(error => {
console.log(error);
this.props.history.push('/errorPage');
});
Note that in case several users are able to sign-in with the same Google Account, you may need to use a Transaction when checking the non-existence of the doc at userRef.

Resources