Here i am trying to register a user with same flat number. But restrict the 2 user can register with same flat number.Here what i wanted to do is check whether any 2 user has register with same flat or not,If yes then no other member will get register and prompt a message and if no user or only 1 user has register then user can successfully get register with the system. my if condition alert value of len but when i put my code inside that condition not alert i got.
here is my code
registerUser(phoneNumber: number) {
alert('flat' + this.flatn.value);
var q = firebase.database().ref('users').orderByChild('flatno').equalTo(this.flatn.value);
q.once('value', (snapshots: any) => {
// alert(JSON.stringify(snapshots.val()));
this.len = snapshots.numChildren();
alert('len ='+this.len);
if(this.len < 2){
this.alert('success');
// i wanted to register user only if this condition gets true. but my code is not being perform as i want which is written after if condition.
}else{
//this.alert('success');
this.alert('More than 2 users are not allowed to register with same flat');
// flatno does not yet exist, go ahead and add new user
}
this.fire.auth
.createUserWithEmailAndPassword(this.email.value, this.password.value)
.then(data => {
let currentUserUid = this.fire.auth.currentUser.uid;
this.uniqueDeviceID.get()
.then((uDid: any) => this.uDid = uDid)
.catch((error: any) => alert('err' + error));
alert(this.uDid);
firebase.database().ref('users/' + currentUserUid).set({
ID: currentUserUid,
email: this.email.value,
password: this.password.value,
first_name: this.fname.value,
last_name: this.lname.value,
contact_no: this.phone.value,
flatno: this.flatn.value,
wing: this.wing.value,
parking_slot: this.vehicle.value,
familyMember: this.familyMember.value,
username: this.user.value,
device_id: this.uDid
});
this.fdb.list("/users_device/").push({
device_id: this.uDid,
Uid: currentUserUid
});
console.log("got data ", data);
//this.alert(data);
this.alert("Registered!");
data.sendEmailVerification().then(
function () {
this.alert("Email Sent Please check your mailbox!");
},
function (error) {
alert("error!");
}
);
this.navCtrl.push(LoginPage);
if (this.authForm.valid) {
let loader = this.loadingCtrl.create({
content: 'Registering...',
dismissOnPageChange: true
});
loader.present();
this.navCtrl.push(LoginPage);
}
})
.catch(error => {
console.log("got an error ", error);
this.alert(error.message);
});
});
}
I do not think that len exists in your scope. check that this property exists and not undefined this.len
Related
I'm working on a signup form. User registers, then in Firebase in the "companies" collection I add a new company, then a "users" collection is added under the just-created company, and the just-created user is added as a document to the company.
Everything is working fine until my code tries to add a new collection "users" under the just created company. Here's my code thus far:
return async (dispatch, getState) => {
try {
const { companyName, email, password } = getState().user;
const response = await Firebase.auth().createUserWithEmailAndPassword(
email,
password
);
if (response.user.uid) {
// Set up company
const company = {
name: companyName,
owner: response.user.uid
};
// Create company
db.collection("companies")
.add(company)
.then(ref => {
console.log("Company ID ", ref.id);
console.log("User ID ", response.user.uid);
// Set up first user
const user = {
uid: response.user.uid,
email: email
};
// Add first User
db.doc(ref.id)
.collection("users")
.add(user);
});
dispatch({ type: SIGNUP, payload: user });
}
} catch (e) {
alert(e);
}
};
Those console.logs return the correct IDs, So it looks like there's an issue with how my reference to the just-created company is being created? How would I fix that?
db.doc(ref.id).collection("users").add(user) may be wrong.
Could you try ref.collection("users").add(user) or db.collection("companies").doc(ref.id).collection("users").add(user);
// Add first User
// db.doc(ref.id)
// .collection("users")
// .add(user);
ref.collection("users").add(user);
// db.collection("companies").doc(ref.id).collection("users").add(user);
See:
https://firebase.google.com/docs/reference/js/firebase.firestore.CollectionReference#add
https://firebase.google.com/docs/reference/js/firebase.firestore.DocumentReference#collection
https://firebase.google.com/docs/reference/js/firebase.firestore.Firestore#doc
In addition to zkohi's correct answer, note that you are mixing the use of async/await with then(), which is not recommended. The following should do the trick (untested):
return async (dispatch, getState) => {
try {
const { companyName, email, password } = getState().user;
const response = await Firebase.auth().createUserWithEmailAndPassword(
email,
password
);
if (response.user.uid) {
// Set up company
const company = {
name: companyName,
owner: response.user.uid,
};
// Create company
const compRef = await db.collection('companies').add(company);
console.log('Company ID ', compRef.id);
const user = {
uid: response.user.uid,
email: email,
};
await compRef.collection('users').add(user);
dispatch({ type: SIGNUP, payload: user });
}
} catch (e) {
alert(e);
}
};
Note how we define the users subcollection:
compRef.collection('users').add(user);
just by calling the collection() method on the company's DocumentReference. This last point is in line with zkohi's answer.
I am trying to send push notifications using cloud functions for a group chat system, but i keep getting this error in my terminal: Each then() should return a value or throw
Why is this happening?
Here's my code:
let functions = require('firebase-functions');
let admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref('/chatrooms/{chatroomId}/chatroom_messages/{chatmessageId}')
.onWrite((snap, context) => {
console.log("System: starting");
console.log("snapshot: ", snap);
console.log("snapshot.after: ", snap.after);
console.log("snapshot.after.val(): ", snap.after.val());
//get the message that was written
let message = snap.after.val().message;
let messageUserId = snap.after.val().user_id;
console.log("message: ", message);
console.log("user_id: ", messageUserId);
//get the chatroom id
let chatroomId = context.params.chatroomId;
console.log("chatroom_id: ", chatroomId);
return snap.after.ref.parent.parent.once('value').then(snap => {
let data = snap.child('users').val();
console.log("data: ", data);
//get the number of users in the chatroom
let length = 0;
for(value in data){
length++;
}
console.log("data length: ", length);
//loop through each user currently in the chatroom
let tokens = [];
let i = 0;
for(var user_id in data){
console.log("user_id: ", user_id);
//get the token and add it to the array
let reference = admin.database().ref("/users/" + user_id);
return reference.once('value').then(snap => {
//get the token
let token = snap.child('messaging_token').val();
console.log('token: ', token);
tokens.push(token);
i++;
//also check to see if the user_id we're viewing is the user who posted the message
//if it is, then save that name so we can pre-pend it to the message
let messageUserName = "";
if(snap.child('user_id').val() === messageUserId){
messageUserName = snap.child('name').val();
console.log("message user name: " , messageUserName);
message = messageUserName + ": " + message;
}
//Once the last user in the list has been added we can continue
if(i === length){
console.log("Construction the notification message.");
let payload = {
data: {
data_type: "data_type_chat_message",
title: "Tabian Consulting",
message: message,
chatroom_id: chatroomId
}
};
return admin.messaging().sendToDevice(tokens, payload)
.then(function(response) {
// See the MessagingDevicesResponse reference documentation for
// the contents of response.
console.log("Successfully sent message:", response);
return response;
})
.catch(function(error) {
console.log("Error sending message:", error);
});
}
});
}
});
});
The message is caused by eslint detecting that you have a then() handler that can finish without returning a value or throwing an error.
It is caused by your for loop on the following line because you do not return a value or throw if data is empty:
for (var user_id in data) {
As others have commented, your for-loop won't execute correctly because you are returning a promise and finishing the handler on only the first iteration.
for (var user_id in data) {
// ...
return reference.once('value').then(snap => {
// ...
}
Arrays in Firebase RTDB
Based on your code, you encountered some of the issues with working with arrays in the RTDB as detailed in this blog post.
Instead of using an array to keep track of a chat room's members (below), it would be best to use a key-value pair approach instead. The value stored in the key-value pair can be a simple true value; or it can be given meaning (true for admins, false for others).
// Array-based list
"chatrooms/chatroomId1": {
"chatroom_messages": { ... },
"users": [
"userId1",
"userId2",
"userId3"
]
}
// RTDB stores above data as:
"chatrooms/chatroomId1": {
"chatroom_messages": { ... },
"users": {
"0": "userId1",
"1": "userId2",
"2": "userId3"
}
}
// Recommeneded: key-value pairs
"chatrooms/chatroomId1": {
"chatroom_messages": { ... },
"users": {
"userId1": true,
"userId2": false,
"userId3": false
}
}
The main benefit of such an approach is that delete a user from a room is simpler which will help with cleaning spam users/messages. To delete a user, you just call
firebase.database().ref("chatrooms/chatroomId1/users/userId1").delete();
rather than
firebase.database().ref("chatrooms/chatroomId1/users").orderByValue().equalTo("userId1").once('value')
.then((snap) => snap.delete());
Furthermore, sending a notification/message that a user was added or removed could be easily implemented using Cloud Functions defined using:
functions.database.ref('/chatrooms/{chatroomId}/users/{userId}').onCreate(...)
functions.database.ref('/chatrooms/{chatroomId}/users/{userId}').onDelete(...)
Chaining promises
When working with asynchronous tasks, avoid using for loops entirely because they are prone to causing undetectable mistakes and modern Javascript provides better alternatives. One such method is using the Promise.all(someArray.map(value => {...})) idiom covered in this answer.
It was also suggested in the question comments to flatten your promise chain, due to the number of changes needed to do the task efficiently, I decided to just make them and note each change in the code itself. The code below relies on the restructure of the chatroom members list discussed above.
let functions = require('firebase-functions');
let admin = require('firebase-admin');
admin.initializeApp(); // CHANGED: Cloud Functions provides the needed environment variables to initialize this for you when called without arguments.
exports.sendNotification = functions.database.ref('/chatrooms/{chatroomId}/chatroom_messages/{chatMessageId}') // CHANGED: renamed 'chatmessageId' to 'chatMessageId' (consistent camelCaseStyling)
.onWrite((change, context) => { // CHANGED: renamed 'snap' to 'change' (matches actual type & less ambiguous below)
if (!change.after.exists()) { // CHANGED: Handle when message was deleted
// message deleted. abort
console.log(`Message #${context.params.chatMessageId} in Room #${context.params.chatroomId} deleted. Aborting.`);
return;
}
let messageData = change.after.val(); // CHANGED: avoid calling change.after.val() multiple times
// console.log("New data written: ", messageData); // CHANGED: Removed verbose log commands.
let message = messageData.message;
let messageAuthorId = messageData.user_id; // CHANGED: renamed 'messageUserId' to 'messageAuthorId' (less ambiguous)
let chatroomId = context.params.chatroomId;
console.log("New message:", { // CHANGED: merged log commands (less StackDriver API overhead when deployed)
user_id: messageAuthorId,
chatroom_id: chatroomId,
message: message
});
let chatroomMembersRef = change.after.ref.parent.parent.child('users'); // CHANGED: only got needed data
return chatroomMembersRef.once('value')
.then(snap => {
// DATABASE STRUCTURE CHANGE: "/chatrooms/{chatroomId}/users" - change array (["userId1", "userId2", "userId3"]) to a userId keyed OBJECT (e.g. {"userId1": true, "userId2": true, "userId3": true})
let chatroomMemberList = Object.keys(snap.val()); // CHANGED: renamed 'data' to 'chatroomMemberList' (less ambiguous)
// console.log("Chatroom Members: ", {
// count: chatroomMemberList.length,
// members: chatroomMemberList
// });
// Asyncronously, in parallel, retrieve each member's messaging token
let chatroomMemberTokenPromises = chatroomMemberList.map((memberId) => { // CHANGED: renamed 'user_id' to 'memberId' (less ambiguous, consistent camelCaseStyling)
let memberDataRef = admin.database().ref("/users/" + memberId); // CHANGED: renamed 'reference' to 'memberDataRef' (less ambiguous)
// CHANGED: For each member, get only their registration token (rather than all of their user data)
let getMessagingTokenPromise = memberDataRef.child('messaging_token').once('value').then((memberTokenSnap) => {
console.log("Got messaging token for member #", memberId);
return memberTokenSnap.val();
});
// If this member is the message author, also get their name to prepend to the notification message.
if (memberId === messageAuthorId) {
let prependUserNamePromise = memberDataRef.child('name').once('value')
.then((memberNameSnap) => {
let messageAuthorName = memberNameSnap.val();
console.log("Message author's name: " , messageAuthorName);
message = messageAuthorName + ": " + message;
});
return Promise.all([getMessagingTokenPromise, prependUserNamePromise])
.then(results => results[0]); // only return result of getMessagingTokenPromise
} else {
return getMessagingTokenPromise;
}
});
// Wait for all of the messaging tokens
return Promise.all(chatroomMemberTokenPromises);
})
.then((chatroomMemberTokensArray) => {
console.log("Constructing the notification message...");
let payload = {
data: {
data_type: "data_type_chat_message",
title: "Tabian Consulting",
message: message,
chatroom_id: chatroomId
}
};
return admin.messaging().sendToDevice(chatroomMemberTokensArray, payload)
.then(function(response) {
// See the MessagingDevicesResponse reference documentation for
// the contents of response.
console.log("Successfully sent message:", response);
return response;
})
.catch(function(error) {
console.log("Error sending message:", error);
});
})
.catch((error) {
console.log("Unexpected error:", error)
});
});
I'm building an iOS messenger app using Swift, Firebase and Nodejs.
My Goal:
Whenever a user sends a message and writes message data (such as senderId, receiverId, messageText) into a Firebase database inside node (/messages/{pushId}/), I want to make a message count increment by 1 using a transaction method that Firebase provides and display a notification to a receiver user.
Progress I've made so far and Problem I'm facing:
I've successfully increment message count (totalCount) using transaction method but I can't get value inside transaction result (Here's image of functions log )
I want to get "value_: 1"( this is the incremented message count) inside snapshot and put it to a badge parameter.
exports.observeMessages = functions.database.ref('/messages/{pushId}/')
.onCreate((snapshot, context) => {
const fromId = snapshot.val().fromId;
const toId = snapshot.val().toId;
const messageText = snapshot.val().messageText;
console.log('User: ', fromId, 'is sending to', toId);
return admin.database().ref('/users/' + toId).once('value').then((snap) => {
return snap.val();
}).then((recipientId) => {
return admin.database().ref('/users/' + fromId).once('value').then((snaps) => {
return snaps.val();
}).then((senderId) => {
return admin.database().ref('/user-messages/' + toId + '/totalCount').transaction((current) => {
return (current || 0) + 1
}).then((readCount) => {
console.log('check readCount:', readCount);
var message = {
data: {
fromId: fromId,
badge: //I want to display the message count here
},
apns: {
payload: {
aps: {
alert: {
title: 'You got a message from ' + senderId.username,
body: messageText
},
"content-available": 1
}
}
},
token: recipientId.fcmToken
};
return admin.messaging().send(message)
}).then((response) => {
console.log('Successfully sent message:', response);
return response;
})
.catch((error) => {
console.log('Error sending message:', error);
//throw new error('Error sending message:', error);
})
})
})
})
Does anyone know how to do this?
Thanks in advance.
The API documentation for transaction() suggests that the promise from the transaction will receive an object with a property snapshot with the snapshot of the data that was written at the location of the transaction. So:
admin.database.ref("path/to/count")
.transaction(current => {
// do what you want with the value
})
.then(result => {
const count = result.snapshot.val(); // the value of the count written
})
So I'm following a Savvy Apps tutorial in order to learn Vue.js. This tutorial uses Firebase with Firestore. Since Firestore is in Beta (as the tutorial says), changes might happen - and I think that might be the case here.
In any case, I'm trying to sign up a new user. I fill out the form and click 'Sign up' and I get this error message:
Error: Function CollectionReference.doc() requires its first argument to be of type string, but it was: undefined
But looking in Firebase, I see that the user has been created. So why do I get this error message? What is the first argument?
The code for signup looks like this:
signup() {
this.performingRequest = true;
fb.auth.createUserWithEmailAndPassword(this.signupForm.email, this.signupForm.password).then(user => {
this.$store.commit('setCurrentUser', user);
// create user obj
fb.usersCollection.doc(user.uid).set({
name: this.signupForm.name,
title: this.signupForm.title
}).then(() => {
this.$store.dispatch('fetchUserProfile');
this.performingRequest = false;
this.$router.push('/dashboard')
}).catch(err => {
console.log(err);
this.performingRequest = false;
this.errorMsg = err.message
})
}).catch(err => {
console.log(err);
this.performingRequest = false;
this.errorMsg = err.message
})
},
Let me know if you need more code - this is the first time I'm testing Vue.js.
createUserWithEmailAndPassword() returns a Promise containing a UserCredential. UserCredential has a property user for the firebase.User object.
You need to make the appropriate changes to your code to correctly access the UID:
signup() {
this.performingRequest = true;
fb.auth.createUserWithEmailAndPassword(this.signupForm.email, this.signupForm.password)
.then(credential=> { // CHANGED
this.$store.commit('setCurrentUser', credential.user); // CHANGED
// create user obj
fb.usersCollection.doc(credential.user.uid).set({ //CHANGED
name: this.signupForm.name,
title: this.signupForm.title
}).then(() => {
this.$store.dispatch('fetchUserProfile');
this.performingRequest = false;
this.$router.push('/dashboard')
}).catch(err => {
console.log(err);
this.performingRequest = false;
this.errorMsg = err.message
})
}).catch(err => {
console.log(err);
this.performingRequest = false;
this.errorMsg = err.message
})
},
This is a real niche question regarding Twitter OAuth with passport.js ()
I have a controller which updates the user's avatar using their Twitter "avatar":
const signInViaTwitter = (twitterProfile) => {
return new Promise((resolve, reject) => {
console.log(twitterProfile);
// find if user exist on in
User.findOne({ username: twitterProfile.username }, (error, user) => {
if (error) { console.log(error); reject(error); }
else {
// user existed on db
if (user) {
// update the user with latest git profile info
user.name = twitterProfile.displayName;
user.username = twitterProfile.username;
user.avatarUrl = twitterProfile.photos.value;
user.email = '';
// save the info and resolve the user doc
user.save((error) => {
if (error) { console.log(error); reject(error); }
else { resolve(user); }
});
}
// user doesn't exists on db
else {
// check if it is the first user (Adam/Eve) :-p
// assign him/her as the admin
User.count({}, (err, count) => {
console.log('usercount: ' + count);
let assignAdmin = false;
if (count === 0) assignAdmin = true;
// create a new user
const newUser = new User({
name: twitterProfile.displayName,
username: twitterProfile.username,
avatarUrl: twitterProfile.photos.value,
email: '',
role: assignAdmin ? 'admin' : 'user',
});
// save the user and resolve the user doc
newUser.save((error) => {
if (error) { console.log(error); reject(error); }
else { resolve(newUser); }
});
});
}
}
});
});
};
The authentication of the user works - but for some reason, the avatar won't show...here is the following console output:
Refused to load the image 'https://api.twitter.com/favicon.ico'
because it violates the following Content Security Policy directive:
"img-src https://abs.twimg.com https://*.twimg.com
https://pbs.twimg.com data:".
Does anyone know what this means? I'm thinking it's probably due to being in development mode - that is, http://localhost:8080/ ... and it won't accept https?? Or won't pass it back?
UPDATE: ^I think the above error is unrelated to the image not being display...
A little look at the html source gives:
<img class="styles__userAvatar___2x2U9" src="{unknown}" alt="Wind Up Lord Vexxos Avatar">
So it's obviously passing in an unknown variable for the src - rather than the user's display avatar...
So, for me it looks like the offending line is:
user.avatarUrl = twitterProfile.photos.value;
What should I be setting this to?
Just a thought, isn't twitterProfile.photos an array? probably you should try accessing twitterProfile.photos[0].value