Im building an app which sends questions to users, at a specific time.
Most of the times everything goes well, and the question appears at the right time on my screen.
But now I'm testing it with Firefox and on my Android device. I'm getting a push notification (on my phone) at the right time, but when I open the app, it still says I dont have any open questions. Same on firefox. When I refresh my internetbrowser, and when I restart the app on my phone, the questions have shown up.
I think this has something to do with the cache of the Meteor app? Does anyone know a proper solution to fix this issue? Or does this only happen when I run my app locally?
My publication looks like this:
Meteor.publish("questions", function () {
var currentUser = this.userId;
if (Roles.userIsInRole(currentUser, ['superadmin']) || Roles.userIsInRole(currentUser, ['admin'])){
return Questions.find({});
}
else if(!this.userId){
return null;
}
else{
var joinedProjects = Projects.find({
$or: [
{invited: { $in: [currentUser] }},
{accepted: { $in: [currentUser] }}
]
}).map(function (projects) { return projects._id; });
return Questions.find({ project: { $in: joinedProjects }});
}
});
Related
I'm working on a simple Meteor app which manages profile pictures, while working in local my project is working fine.
But after deploying it on an AWS EC2 instance (With Meteor-Up), every works fine, except the image upload.
I'm using CollectionFS (cfs:standard-packages, cfs:gridfs).
Here's the code :
// Collection definition
Images = new FS.Collection("images", {
stores: [new FS.Store.GridFS("original")]
});
Images.allow({
'insert': function () {
return true;
},
'update': function () {
return true;
},
'download':function(){
return true;
}
});
// Event to upload the image
"click .js-validate"(event, instance){
Images.insert(instance.file.get(), function (err, fileObj) {
if(err){
console.log("Error");
console.log(err);
}else{
console.log("No error");
console.log(err);
Meteor.call('updatePP', fileObj._id, function(){
Modal.hide("Modal_update_pp");
});
}
});
}
The security rules for my EC2 instance :
And here's the error showing when i execute the event which execute the Images.insert function.
The error ERR_CONNECTION_REFUSED is showing.
I'm able to get the image id, but the image isUploaded function return false :
Why am i having this error and how can i solve this problem ?
Is it due to my EC2 configuration or the code inside my Meteor Project ? Thanks !
I’d suggest to switch to ostrio:files which is fully documented and up to date. GridFS is really old and buggy. You can then save your files securely to a S3 instance and keep your MongoDB size low.
I built an app with simple user login, using Accounts-unstyled. It all works fine, except that the template showing user data is not re-rendered with the user logs in. However if the user reloads the page, then his data is present.
My code doesn't really contain anything related to user, except for fetching the correct user's data. Do I need to manually trigger a refresh after the login?
The app is at www.barelinks.in. Test login: user=test; password=pwdpwd.
Upon first login nothing changes, but if you reload you will see the user's data.
Here is the code for the subscription:
if(Meteor.isServer) {
Meteor.publish('links', function linksPublication(userId) {
return Links.find({ user: userId });
});
}
And the helper fetching the right data (the Regex is for the search feature):
Template.body.helpers({
links: function() {
var nbSkip = linksPerPage*(Template.instance().page.get() - 1);
var queryRegex = new RegExp(Template.instance().query.get(), 'i');
return Links.find({ user: Meteor.userId(), title: queryRegex }, { sort: { createdAt: -1 }, limit: linksPerPage, skip: nbSkip });
},
});
Thanks!
I recognized that the content is updated when refreshing the page in a logged in state. Than you can switch forth and back and everything works as expected. I came usually across this behavior when the data subscription is not as it should be.
The server automatically updates the data according to a change of logged in state, you need to rely on the this.user property in the publish function.
if(Meteor.isServer) {
Meteor.publish('links', function linksPublication() {
return Links.find({ user: this.userId });
});
}
if people log into my Meteor 1.3.1 app from more than one tab/browser they're going to have problems.
So, when a user logs in, I'd like to log them out everywhere else, and park them on a "you logged in somewhere else" page.
I tried a couple things: Accounts.logoutOtherClients() looked promising, but seems to be broken. This logs everyone out.
if (Meteor.isClient) {
// FF 1 tab, Chrome 2 tabs.
// FF login -> chrome logs out ok.
// Chrome login -> FF logs out. Then Chrome other tab logs in. Then both Chrome tabs logout.
Accounts.onLogin(function() {
console.log(Meteor.loggingIn()); // false. See https://github.com/meteor/meteor/issues/1616
Accounts.logoutOtherClients();
});
}
Before that I was working on a hand-rolled solution, but it seemed like Meteor.logout() would logout both tabs in the same browser. The best I could get was window.location="https://google.com"
NOTE: The approach below doesn't work if user closes all tabs. When they re-open app, they don't have a tabToken, so can never login again.
What's the way to do this?
The best way I can think to do this is:
1) At Meteor.start() on the client make a unique tabToken
2) Save token to Session variable on client
3) At login save tabToken to user
4) On client, listen to Meteor.user().profile for tabToken changes.
if (Meteor.isClient) {
// Make a unique tabToken at startup.
Meteor.startup(function() {
var tabToken = (0|Math.random()*9e6).toString(36); // jshint ignore:line
Session.set('tabToken', tabToken);
console.log('startup TabToken ' + Session.get('tabToken'));
});
// onLogin, set the active tab to our tabToken
Accounts.onLogin(function() {
Meteor.call('setActiveTab', Session.get('tabToken'), function() {
// When method returns, watch profile for tabToken changes
console.log('setActiveTab tabToken ' + Session.get('tabToken'));
console.log('setActiveTab Meteor.user().profile.tabToken ' + Meteor.user().profile.tabToken);
Tracker.autorun(function (c) {
// If the tabToken changes to a different token, logout
if (Meteor.user()) {
var userTabToken = Meteor.user().profile.tabToken;
if (userTabToken && !Session.equals('tabToken', userTabToken)) {
window.location = 'https://google.com';
// Doesn't work
// c.stop();
// Meteor.logout(); // ERROR this logs out all tabs in browser.
// Router.go('loggedOut');
}
}
});
});
});
}
// Make a meteor method to set tabToken
if (Meteor.isServer) {
Meteor.methods({
setActiveTab: function(tabToken) {
if(!tabToken) throw new Meteor.Error('noTabToken');
console.log('setActiveTab ' + tabToken);
if (!Meteor.user()) return;
Meteor.users.update(Meteor.userId(), {
$set: {
'profile.tabToken': tabToken
}
});
}
});
}
This just seems really kludgy. Any other suggestions?
Mike
I've been using Firebase extensively and still face only one real issue: onDisconnect isn't 100% reliable in my experience.
If you close a computer without closing the window first, or kill the browser, you sometime have the "garbage collector" getting your onDisconnect executed, sometimes it doesn't.
My question is the following: I just don't use /.connected for now, I basically use a simple
userRef.set('status', 1);
userRef.onDisconnect().update({ 'status' : 0 });
Is there anything wrong with this approach? Do we agree that the update parameters are passed to the server at the time the line is executed and not before window unload ?
NB: I happen to try to keep a multi-window status, using the following approach to keep the status at 1 if another window is closed:
userRef.child('status').on('value', function(snap) {
if (snap.val() != 1) {
userRef.set('status', 1);
}
});
I don't this how this could be related, but...
MY SOLUTION: In fact, I had just missed the part where you learn that onDisconnect is only triggered once. To get a persistent onDisconnect, you need to implement basic persistence.
Helpers.onConnected = function(callback) {
var connectedRef = lm.newFirebase('.info/connected');
var fn = connectedRef.on('value', function(snap) {
if (snap.val() === true) {
if (callback) callback();
}
});
var returned = {};
returned.cancel = function() {
connectedRef.off('value', fn);
};
return returned;
};
Simple use case:
this._onConnected = lm.helpers.onConnected(function() {
this.firebase.onDisconnect().update({ 'tu': 0 });
}.bind(this));
And then to cancel:
if (this._onConnected) this._onConnected.cancel();
this.firebase.onDisconnect().cancel();
You should always call the onDisconnect() operation BEFORE you call the set() operation. That way if the connection is lost between the two you don't end up with zombie data.
Also note that in the case where the network connection is not cleanly killed, you may have to wait for a TCP timeout before we're able to detect the user as gone and trigger disconnect cleanup. The cleanup will occur, but it may take a few minutes.
After a lot of digging around, I found this issue which was actually happening in my case and I think for most of the others also who are coming to this page for a solution.
So the problem is firebase checks it's accessToken twice when 1. the onDisconnect is enqueued and 2. when the onDsiconnect is applied.
Firebase doesn't proactively refreshes tokens when a tab is not visible. If the page is inactive for more than the expiry of the accessToken and closed without focusing on that tab firebase will not allow the onDisconnect because of the expired accessToken.
Solutions to this:
You can get a new accessToken by setting some sort of
interval like this:
let intervalId;
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
if (intervalId) {
window.clearTimeout(intervalId);
intervalId = undefined;
}
} else {
firebaseApp.auth().currentUser.getIdToken(true);
intervalId = setInterval(() => {
firebaseApp.auth().currentUser.getIdToken(true);
}, intervalDuration);
}
});
Or you can disconnect the database manually firebase.database().goOffline() whenever tab visibility changes from "visible".
Expanding on the comment :
This is why we provide the /.info/connected endpoint.
We recommend re-establishing the disconnect handler every time the connection comes back online
I followed the above and got it fixed:
const userRef = Firebase.database().ref('/users/<user-id>')
Firebase.database()
.ref('.info/connected')
.on('value', async snap => {
if (snap.val() === true) {
await userRef.onDisconnect().remove();
await userRef.update({ status : 'online' })
} else {
await this.userRef.remove()
}
});
For reference, my previous code was:
const userRef = Firebase.database().ref('/users/<user-id>')
userRef.onDisconnect().remove();
await userRef.update({ status : 'online' })
The issue with this is that the onDisconnect may not work when you go offline and come back online multiple times
I have a problem with push notifications unregister. I am able to register the device and get the token, also sending notifications too, but I would like to add the unregister feature too. Here is the code y wrote for it:
var unsubscribeNotification = function unsubscribeNotification() {
try {
pushNotification.unregister(
function(e) {
//unRegister Success!!!
alert('unRegister Success');
},
function(e) {
//unRegister Failed!!!
alert('unRegister Failed');
});
}
catch(err) {
//Handle errors here
alert(err.message);
}
}
I also put a button to run the unsubscribeNotification() function when you click it. After a few seconds the application stops, no error, no messages, nothing!
I am using a Galaxy S3, I think it has Android 4.1
Thanks for the help!