I want to detect if a logged in user has no records in the collection and then start them with some default values. So I use this at the beginning of my isClient part;
if (Meteor.isClient) {
if(Meteor.user()){
Meteor.subscribe('collection');
var currentUserId = Meteor.userId();
if (collection.find({"userID": currentUserId}).count() == 0)
{
Meteor.call('initUser', currentUserId);
}
} else {
console.log("You are not logged in");
}
}
Problem is that it never see's me as logged in. Do I need to be calling this from a template or something? In the tutorial that I did they just had it all by itself.
Your code looks good to me but it does not live in a reactive computation, meaning that it's going to run only once at the beginning of your code and never again, just like regular sequential programming.
You need to surround your code with a Tracker.autorun like this :
if (Meteor.isClient) {
Tracker.autorun(function(){
// Meteor.userId() is a reactive data source that will trigger invalidation
// of the reactive computation whenever it is modified (on login/logout)
var currentUserId = Meteor.userId();
if(currentUserId){
Meteor.subscribe('collection',function(){
if (collection.find({
userID: currentUserId
}).count() === 0){
Meteor.call('initUser', currentUserId);
}
});
} else {
console.log("You are not logged in");
}
}
}
I've refactored it to only use Meteor.userId() because you don't use the currentUser properties in this piece of code.
Related
I have a mobile app in development and I'm transforming one of the collections to get the user last seen time, avatar etc.
PlayerRecord.prototype = {
constructor : PlayerRecord,
getAssociatedUser: function () {
return Meteor.users.findOne( this.user_id );
},
lastSeenFormatted: function () {
var user = this.getAssociatedUser();
return (user && user.last_seen) ? user.last_seen : 'Never';
}
}
My problem is that, if the user last seen returns Never initially but then the user is seen, my string return over there is not updated...obviously.
How would you advise me to handle this situation?
Did you check whether any user had a value for last_seen? This field has to be explicitly published.
According to the Meteor docs (http://docs.meteor.com/#/full/meteor_user):
By default, the current user's username, emails and profile are
published to the client. You can publish additional fields for the
current user with:
// server
Meteor.publish("userData", function () {
if (this.userId) {
return Meteor.users.find({_id: this.userId},
{fields: {'last_seen': 1}});
} else {
this.ready();
}
});
// client
Meteor.subscribe("userData");
I have publish method that depends on user state:
Meteor.publish('myGroup', function () {
if (this.userId != null) {
var user = Meteor.users.findOne(this.userId);
return Group.find(user.profile.groupId);
} else {
return [];
}
});
When user state changes (user.profile.groupId), I don't get new data. I can fix it by refreshing the browser.
I was trying to fix that, by making subscription reactive:
Tracker.autorun(function () {
var user = Meteor.user(); // depend on user
if (user != null) {
Meteor.subscribe('myGroup');
}
});
But it doesn't seem to work. What is the best way to solve this problem?
Passing unused parameters seems to be the best workaround:
Meteor.subscribe("myGroup", user.profile.groupId);
In my Meteor app I use the default accounts package, which gives me the default login and registration functionality. Now I want to add an extra field to user, say nickname, and for the logged in user the possibility to edit this information.
For editing the profile I suppose I should be doing something like this:
Template.profileEdit.events({
'submit form': function(e) {
e.preventDefault();
if(!Meteor.user())
throw new Meteor.Error(401, "You need to login first");
var currentUserId = this._id;
var user = {
"profile.nickname": $(e.target).find('[name=nickname]').val()
};
Meteor.users.update(currentUserId, {
$set: user
}, function(error){
if(error){
alert(error.reason);
} else {
Router.go('myProfile', {_id: currentUserId});
}
});
}
});
But I doesn't store the info if I look in Mongo. Also when showing the profile, {{profile.nickname}} returns empty. What is wrong here?
Edit: added collections\users.js to show permissions:
Meteor.users.allow({
update: function (userId, doc) {
if (userId && doc._id === userId) {
return true;
}
}
});
Meteor.users.deny({
update: function(userId, user, fieldNames) {
return (_.without(fieldNames, 'profile.nickname').length > 0);
}
});
Yeah, I believe that should do the job, although I haven't actually run the code. The idea is certainly right.
The main things to be aware of are:
The necessity to allow the user doc to be edited from the client with an appropriate Meteor.users.allow() block on the server, assuming you're going to remove the "insecure" package (which you need to before doing anything in production).
The fact that "by default the server publishes username, emails, and profile", so you'll need to write a Meteor.publish function on the server and subscribe to it if you want to expose any other fields within the user document to the client once you've removed the "autopublish" package (which again, you really should).
I'm building a messenger application, and before a conversation is created I want to verify if a user exists. If it does, then it will create the conversation. If not, then it should return an error. I've been working with this code on the server side but for some reason it won't work. I've tried many different tweaks, but this is basically my structure:
Meteor.methods({
createConversation: function(secondPerson) {
function doesUserExist(secondPerson) {
var userx = Meteor.users.findOne({username: secondPerson});
if (userx === secondPerson) {
return false;
} else {
return true;
}
}
if (doesUserExist()) {
Conversations.insert({
person1: Meteor.user().username,
person2: secondPerson
});
} else {
Conversations.insert({
person1: "didn't work"
});
}
}
});
The main point you were missing is that find returns a cursor, whereas findOne returns a document. Here is one way to implement the method:
Meteor.methods({
createConversation: function(username) {
check(username, String);
if (!this.userId) {
throw new Meteor.Error(401, 'you must be logged in!');
}
if (Meteor.users.findOne({username: username})) {
return Conversations.insert({
person1: Meteor.user().username,
person2: username
});
} else {
throw new Meteor.Error(403, username + " does not exist!");
}
}
});
Note the following features:
validates that username is a string
requires that the user be logged in to create a conversation
reduces the user existence check to a single line
returns the id of the new conversation
uses Meteor.Error with explanations which can be seen on the client
To use it just open your browser console and try making calls like:
Meteor.call('createConversation', 'dweldon', function(err, id){console.log(err, id);});
I'm using the accounts-ui package and would like to process some javascript as soon as the user is logged in/and or registered.
Is there an event that gets called as soon as the user signs in?
The raw login API (eg loginWithFacebook, loginWithPassword, etc) has a callback that fires when login is complete, but this is not currently exposed through accounts-ui. This may change.
A potential workaround, inspired by Werner's suggestion, but also taking page load into account:
var oldUserId = undefined;
Meteor.autorun(function() {
var newUserId = Meteor.userId();
if (oldUserId === null && newUserId) {
console.log('The user logged in');
} else if (newUserId === null && oldUserId) {
console.log('The user logged out');
}
oldUserId = Meteor.userId();
});
You could check the result of the Meteor.userId() function inside Meteor.autorun():
Meteor.autorun(function() {
if (Meteor.userId()) {
console.log('The user logged in');
}
});
Just to give an alternative; I monkey patched the callback function. It looks a bit more complex because the credentialRequestCompleteHandler requires a function that returns a function but besides that its a plain monkey patch. Stick this in main.js or something that gets processed late and only once. I hope it helps for future reference.
var orgCallback = Accounts.oauth.credentialRequestCompleteHandler;
Accounts.oauth.credentialRequestCompleteHandler = function(callback){
return function (credentialTokenOrError) {
var tmpFunc = orgCallback(callback);
tmpFunc(credentialTokenOrError);
alert("do your own thing here");
}
}