How can I update a document in Firestore on beforeunload?
function sleep() {
var start = new Date().getTime();
while (new Date().getTime() < start + 3000);
}
window.addEventListener("beforeunload", function(event) {
const ref = db.collection('users').doc(currentUser.uid);
ref.update({
profiles: true
}).then(function() {
console.log("Added!");
});
console.log("Blocking for 3 seconds...");
sleep();
console.log("Done!!");
});
Seems like ref.update() is never called? Any idea why that's the case?
Related
I'm trying to create a cloud function for firebase that remove a user depending on the delay value and insert back the after the number of delay.
exports.delayqueue = functions.database.ref('/queues/{queueid}/members/{memberid}').onWrite(event => {
var members = event.data.ref.parent;
var user = event.data;
var queueid = members.parent;
var userid = event.params.memberid;
var delayfor = user.child('delay').val();
var name = user.child('name').val();
if(delayfor != 0){
members.child(event.params.memberid).remove();
join(userid,queueid,delayfor,name);
return;
}else{
return;
}
});
function join(userid,queueid,delayfor,name){
setTimeout(function(){
var ref = db.ref("queues/queueid/members/userid");
ref.set({
name: name,
timestamp: Date.now(),
delay : 0
});
}, delayfor*1000);
};
But it's not working can someone help?
You'll need to wrap your setTimeout in a Promise:
exports.delayqueue = functions.database.ref('/queues/{queueid}/members/{memberid}').onWrite(event => {
var members = event.data.ref.parent;
var user = event.data;
var queueid = members.parent;
var userid = event.params.memberid;
var delayfor = user.child('delay').val();
var name = user.child('name').val();
if (delayfor !== 0){
members.child(event.params.memberid).remove();
return join(userid,queueid,delayfor,name);
} else {
return;
}
});
function join(userid,queueid,delayfor,name){
return new Promise((resolve, reject) => {
setTimeout(function(){
var ref = db.ref("queues/queueid/members/userid");
ref.set({
name: name,
timestamp: Date.now(),
delay : 0
}).then(resolve, reject);
}, delayfor*1000);
});
};
Note that the time spent waiting for setTimeout is billed as function execution time and is also subject to the function timeout. If you're only delaying a few seconds, that might be okay, but if the delay is expected to be minutes this solution isn't going to be viable or cost-effective.
This Meteor code is giving the error:
Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.
I tried Meteor.bindEnvironment for no avail and want to try Meteor.wrapAsync. I could not figure it out from the docs. Could some one please help me with the syntax? thx
Meteor.methods({
'createTransaction':
function (nonceFromTheClient, Payment) {
let user = Meteor.user();
gateway.transaction.sale(
{
arg_object
},
function (err, success) {
if (!err) {
//do stuff here
}
}
);
}
});
Wrap in Meteor.wrapAsync
Meteor.methods({
'createTransaction':
function (nonceFromTheClient, Payment) {
this.unblock();
let user = Meteor.user();
var sale = Meteor.wrapAsync(gateway.transaction.sale);
var res = sale({arg_object});
future.return(res);
return future.wait();
}
});
Or try wrapping it in Fiber
var Fiber = Npm.require('fibers');
Meteor.methods({
'createTransaction': function (nonceFromTheClient, Payment) {
Fiber(function() {
let user = Meteor.user();
gateway.transaction.sale(
{
arg_object
},
function (err, success) {
if (!err) {
//do stuff here
}
}
);
}).run()
}
});
Update: Here's how I handle stripe with Async.runSync and Meteor.bindEnvironment
var stripe = require("stripe")(Meteor.settings.private.StripeKeys.secretKey);
Meteor.methods({
'stripeToken': function() {
this.unblock();
var future = new Future();
var encrypted = CryptoJS.AES.encrypt(Meteor.userId(), userIdEncryptionToken);
future.return(encrypted.toString());
return future.wait();
},
'stripePayment': function(token) {
var userId = Meteor.userId();
var totalPrice = 0;
//calculate total price from collection
totalPrice = Math.ceil(totalPrice * 100) / 100;
userEmail = Meteor.users.findOne({
'_id': userId
}).emails[0].address;
// Create a charge: this will charge the user's card
var now = new Date();
Async.runSync(function(done) {
var charge = stripe.charges.create({
amount: Math.ceil(totalPrice * 100), // Amount in cents // coverting dollars to cents
currency: "usd",
source: token,
receipt_email: userEmail,
description: "Charging"
}, Meteor.bindEnvironment(function(err, charge) {
if (err) {
//handle errors with a switch case for different errors
done();
} else {
//handle res, update order
}
}));
}); // Async.runSync
},
});
#DavidWeldon I tried your code (Meteor observe changes added callback on server fires on all item) and it's very good thank you !
However I would like to have your advise : I use it for desktop notifications : when i get one notification, there is one console log (ok), but when i get another notification (total : 2), there are two console log (I want only one console log because there is only a +1 notification)
Here is my code :
if (Notification.permission !== "granted")
Notification.requestPermission();
var query = Notifications.find({userId: Meteor.userId(), read: false});
(function() {
var initializing = true;
query.observeChanges({
added: function(id, notification) {
if (!initializing) {
console.log(notification);
}
}
});
initializing = false;
})();
Thank you for your help ! :)
You could use another flag in there?
if (Notification.permission !== "granted")
Notification.requestPermission();
var query = Notifications.find({userId: Meteor.userId(), read: false});
(function() {
var initializing = true;
var firstNotif = true;
query.observeChanges({
added: function(id, notification) {
if (!initializing && firstNotif) {
firstNotif = false;
console.log(notification);
}
}
});
initializing = false;
})();
Finally I found an answer, exploring this post in discover meteor : https://www.discovermeteor.com/blog/template-level-subscriptions/
(in my question, I was working in Template.notifications.helpers)
Here's my new code :
Template.notifications.onCreated(function () {
if (Notification.permission !== "granted")
Notification.requestPermission();
var instance = this;
instance.autorun(function () {
var query = Notifications.find({userId: Meteor.userId(), read: false});
query.observeChanges({
added: function(id, notification) {
var notification = new Notification('Notification', {
icon: '',
body: "You got a new notification !"
});
}
});
});
});
I have this piece of code using on() to get data from Firebase, inside on() I create object which I want to send out of function for future use - using return, but it seems it doesn't return anything.
So question is how can I make it right?
postsRef.on('value', function(snapshot) {
if (snapshot.val() === null) {
var allPosts = false,
numberOfPosts = 0;
}
else {
var allPosts = snapshot.val(),
numberOfPosts = Object.size(allPosts);
}
var postsData = {
content: allPosts,
count: numberOfPosts
};
return postsData;
});
The callback function is called asynchronously (some time in the future). So by the time it is invoked, postsRef.on(...) has already returned and any code immediately after it will have run.
For example, this might be tempting, but would not work:
var postsData;
postsRef.on('value', function(snapshot) {
postsData = snapshot.val();
});
console.log(postsData); // postsData hasn't been set yet!
So there are a few different ways to tackle this. The best answer will depend on preference and code structure:
Move the logic accessing postsData into the callback
postsRef.on('value', function(snapshot) {
postsData = snapshot.val();
console.log(postsData);
});
Call another function when the callback is invoked
function logResults(postsData) {
console.log(postsData);
}
postsRef.on('value', function(snapshot) {
logResults(snapshot.val());
});
Trigger an event
function Observable() {
this.listeners = [];
}
Observable.prototype = {
monitorValue: function( postsRef ) {
var self = this;
postsRef.on('value', function(snapshot) {
self._notifyListeners(postsRef);
});
},
listen: function( callback ) {
this.listeners.push(callback);
},
_notifyListeners: function(data) {
this.listeners.forEach(function(cb) {
cb(data);
}
}
};
function logEvent( data ) {
console.log(data);
}
var observable = new Observable();
observable.listen( logEvent );
observable.monitorValue( /* postsRef goes here */ );
The following code does not update the database everytime a tweet is found - it silently hangs, adding no tweets to the database.
If a tweet is manually added to the DB from the JS console in the browser, it shows up just fine, but no tweets are being added to the DB automatically.
Tweets = new Meteor.Collection("tweets");
if (Meteor.isClient) {
Template.kildeer.tweets = function () {
return Tweets.find({});
};
}
if (Meteor.isServer) {
Meteor.startup(function () {
var require = __meteor_bootstrap__.require,
Twit = require('twit')
, T = new Twit({
consumer_key: 'blahblah',
consumer_secret: 'blahblah',
access_token: 'blahblah',
access_token_secret: 'blahblah'
});
var stream = T.stream('statuses/filter', { track: ['bing', 'google', 'microsoft'] })
stream.on('tweet', function (tweerp) {
var id;
console.log(tweerp.text);
id = Tweets.insert({text: tweerp.text, screen_name: tweerp.user.screen_name, profile_image: tweerp.user.profile_image_url});
console.log(id);
});
});
}
In Meteor, Collection.insert must always be called inside of a Fiber() closure.
Fiber(function() {
Tweets.insert({text: tweerp.text, screen_name: tweerp.user.screen_name, profile_image: tweerp.user.profile_image_url});
}).run();