Alerts/Notifications on Meteor application do not appear - javascript

I created a new "Alerts" collection. Its to show number of unread messages. Messages get submitted and appear, and theres no other error on console or server.
2nd issue is, when i click on the specific room, it is supposed to mark all new messages as "read". Somehow the number stays. Error shows Exception in queued task: .added#http://localhost:3000/app/lib/collections/messages.js
File structure:
roomList.js - shows a list of all rooms, shows number of unread messages
roomDetail.js - when click specific room in list, will mark message as
"read", unread number dissapears.
alerts.js (Alerts collection)
messages.js (Messages collection)
rooms.js (Rooms collection)
publications and sub js
Meteor.publish('alerts', function() {
return Alerts.find({ userId: this.userId, read: false });
});
Meteor.subscribe('alerts')
Alerts collection js
Alerts = new Mongo.Collection('alerts');
Alerts.allow({
update: ownsDocument,
//if removed, shows error:
// insert failed: Access denied. No allow validators set on restricted collection for method 'insert'.
insert: function(){
return true;
}
});
createMessageAlert = function(message) {
if ( message.user !== Meteor.userId() ){
Alerts.insert({
userId : message.user,
roomId : Router.current().params._id, //params id of current room
messageId : message._id,
read : false
});
}
};
roomDetail.js
Messages.insert({
roomId : Router.current().params._id,
msg : message,
user : Meteor.user()._id
});
template.find('input').value = '';
createMessageAlert(message);
roomsList.js
Template.list.helpers({
alerts: function (){
return Alerts.find({ userId: Meteor.userId(), read: false });
},
alertCount: function(){
return Alerts.find({ userId: Meteor.userId(), read: false }).count();
}
});
Template.allRooms.events({
'click a': function() { //click the a href to go into roomDetail
Alerts.update(this._id, {$set: {read: true}});
}
});

Ultimate solution :
You should call the createMessageAlert from a trigger when a new Message is added in Messages collection.
Pre-requisites:
create a trigger(MSG_OBSERVER) for Messages collection, where whenever anything is added to the collection, a createMessageAlert method is invoked provided with the added document object, so you can play inside the method and do desired operations.
When you are updating Alerts collection. The collection should be published in such a way(named as "null") that it should be reactive and should be available from all the instances accessing the same account from different browser instances.
Implementation
Just add below code in your collections.js
Meteor.method(
'createMessageAlert': function(id, fields) {
if ( fields.user !== Meteor.userId() ){
Alerts.insert({
userId : fields.user,
roomId : Router.current().params._id, //params id of current room
messageId : id,
read : false
});
}
}
);
var MSG_OBSERVER = Messages.find();
MSG_OBSERVER.observe({
added: function(id, fields){
Meteor.call('createMessageAlert', id, fields);
}
});
Meteor.publish(null ,function() { // null name means send to all clients
//use Messages.insert() to generate a fake message and add data in below params
Meteor.call('createMessageAlert', id, fields);
return Alerts.find();
});
Explaination
If you again read the pre-requisites, you will understand the code. Ensure you are subscribed with desired collection on client side. This code makes every collection involved and triggers very reactive and responsive.
Whatever you will add as messages will be added to Alerts as well.
Publishing "null" will simply publish data to all clients making UI behavior more robust and asynchronous.(I am using this feature in displaying real-time graphs, you don't even have to refresh UI and your data gets reflected.)
While publishing "null", you can create a fake Message OBJ and add it to call createMessageAlert function. You have to do this because you have to initiate publish on server restarts. choose Message Obj wisely so that it won't impact the work flow.

Related

Parse Server Cloud Code Update User

I am attempting to update a parse user field and the function stops in the middle of it:
Parse.Cloud.define("modifyAdminStatus", function(request, response) {
var userQuery = new Parse.Query(Parse.User);
var isAdmin = request.params.adminStatus;
console.log("isAdmin:" + isAdmin);
userQuery.equalTo("username", request.params.username);
userQuery.find({ useMasterKey: true,
success: function(user) {
console.log(user.length);
console.log("Got User")
console.log(user);
user.set("isAdmin", isAdmin);
console.log("Set Status");
user.save(null, {useMasterKey: true,
success: function(user) {
response.success();
},
error: function(error) {
response.error(error.message);
}
});
},
error: function(error) {
response.error(error.message);
}
});
});
I dont get any syntax errors, when i run the code i get:
1
Got User
[ ParseUser { _objCount: 2, className: '_User', id: '2vigcitsl6' } ]
in my console. However, it seems to stop the code after i attempt to set the admin status. I have tried running it using useMasterKey but that didnt do anything so maybe I'm missing something and where the useMasterKey should go?
The answer is:
query.find({
... code here
});
Returns an array, using query.first (or selecting one object from the array) instead will get one object and allow you to set things on it.
When you're trying to save the user, parse expects two parameters. The first should be an object containing any changes, and the second should be the save options.
So in your case, simply change your save to user.save (null, {useMasterKey:true, success...})
The way you have it now would create a column on Parse.User entitled useMasterKey, if permissions allow.

Collection helper throws a not defined error

Im trying to set up a list of "rooms". The intended sequence:
Click name of the user on his/her profile page
Check for existing room. If yes, go to that room, if not set up new room.
Im using both dburles:collection-helpers and reywood:publish-composite.
Its throwing me this error.
TypeError: Cannot read property 'username' of undefined
at Document.Rooms.helpers.recName (rooms.js:18)
And line 18 is:
return Meteor.users.findOne({ _id: this.receiver }).username;
i.e. _id: this.receiver is undefined.
I also tried to add protective checks in the collection helpers but error remains. I.e. return user && user.username for example.
One thing I note is that, I noticed when I click on the user, it goes to the room linked to the user's id. However when I click back, it jumps to a blank room with a different id thats unrecognised.
The relevant codes:
Server publish
Meteor.publish("onlusers", function (){
return Meteor.users.find({});
});
Rooms.js collection helper
Rooms.helpers({
recName: function() {
return Meteor.users.findOne({ _id: this.receiver }).username;
}
});
User.js (for profile page events)
Template.usersShow.events({
'click .user': function() {
var receiver = this._id;
Session.set('chatId', this._id);
var res = Rooms.findOne({
$or: [
{ owner : this._id },
{ receiver : this._id }
]
});
if(res){
Router.go('roomDetail', { "_id" : res._id });
} else {
var newRoom = Rooms.insert({
owner : Meteor.userId(),
receiver : receiver,
username : Meteor.user().username,
});
Session.set('roomid', newRoom);
Router.go('roomDetail', { "_id" : newRoom });
}
}
});
Your diagnosis:
_id: this.receiver is undefined.
May be misleading. What is also possible is that the user subscription isn't completely loaded when your helper runs. I was helping someone else with a similar problem with publish-composite the other day - the subscription is marked as ready when the parents are ready but the children may not have finished loading yet. I think of this as a bug in publish-composite, all the related objects really need to be there before the subscription can be marked as ready.
Instead of returning:
return Meteor.users.findOne({ _id: this.receiver }).username;
You can do:
var user = Meteor.users.findOne({ _id: this.receiver });
return user && user.username;
So you'll get nothing back until the user object loads but you won't throw an error.

How to prevent current user get notified?

I'm making an app that allows user to like and comment on other user post. I'm using Parse as my backend. I'm able to notified user everytime their post liked or commented. However if current user like or comment on their own post this current user still notified. How can I prevent this?
Here is the js code that I use:
Parse.Cloud.afterSave('Likes', function(request) {
// read pointer async
request.object.get("likedPost").fetch().then(function(like){
// 'post' is the commentedPost object here
var liker = like.get('createdBy');
// proceed with the rest of your code - unchanged
var query = new Parse.Query(Parse.Installation);
query.equalTo('jooveUser', liker);
Parse.Push.send({
where: query, // Set our Installation query.
data: {
alert: message = request.user.get('username') + ' liked your post',
badge: "Increment",
sound: "facebook_pop.mp3",
t : "l",
lid : request.object.id,
pid: request.object.get('likedPostId'),
lu : request.user.get('username'),
ca : request.object.createdAt,
pf : request.user.get('profilePicture')
}
}, {
success: function() {
console.log("push sent")
},
error: function(err) {
console.log("push not sent");
}
});
});
});
If I understand the context of where this code is correctly,
I recommend checking
if request.user.get("username") != Parse.CurrentUser.get("username")
Before sending out the push notification
Where is your cloud function being called from? If you're calling it from your ios code, then before you call the cloud code function, just prelude it with something like this:
if (PFUser.currentUser?.valueForKey("userName") as! String) != (parseUser.valueForKey("userName") as! String)

Meteor collection insert not updating on client

I'm currently having an issue with a collection insert in Meteor. I call a method to insert a new item into a collection. The server database shows the new item but the client side has no record of the collection. I've found if I refresh the page, my template referencing the collection populates and works.
Here is the method inserting the item located at 'lib/collections/items.js'
Items = new Mongo.Collection("items");
Meteor.methods({
addItem: function (text) {
if (! Meteor.userId()){
throw new Meteor.Error("not-authorized");
}
console.log(text);
console.log(Meteor.userId());
console.log(Meteor.user().username);
console.log(Meteor.user().household);
Items.insert({
text: text,
createdAt: new Date(), //current time
owner: Meteor.userId(),
username: Meteor.user().username,
household: Meteor.user().household
});
},
Here is the server publishing of the items located at '/server/main.js'
Meteor.publish("items", function(){
if(typeof Meteor.users.findOne({'_id': this.userId}) === 'undefined') return null;
return Items.find( {household : Meteor.users.findOne({'_id': this.userId}).household});
});
Here is the code calling the method located at 'client/main.js'
Template.body.events({
"submit .new-task": function (event) {
// This function is called when the new task form is submitted
var text = event.target.text.value;
text = text.trim();
if(text)//Check for non-null, non-empty
{
text = capitalizeEachWord(text);
console.log(text);
Meteor.call("addItem",text);
}
//Clear form
event.target.text.value = "";
//Prevent default form submit
return false;
},
Here is where the collection is queried from to display in a template at 'client/templates/itemList.js'
Template.itemList.helpers({
items: function() {
console.log('Trying to subscribe');
return Items.find({}, {sort : {checked: 1, createdAt: -1}});
}
I'm just starting to learn Meteor. I appreciate any help!
Edit
Thanks for the responses,
I've tried these suggestions and still getting the same result. I've tried a different way of relating users to households now and still having the same issues. I can insert an item from the client, it exist on the server side, and yet it doesn't exist on the client side. I'm sure i'm missing something small here but for the life of me, can't find it.
I know this is a huge dump of info but here is my updated source code (still producing the same problem) and logs. I've tried to put everything I know about this issue. Thanks for any help! I'm just starting out with Meteor and I know no one who develops in it so troubleshooting is a bit tough right now.
Client Specific Code
Located at 'client/main.js'
Template.body.helpers({
householdId: function() {
//return Meteor.users.findOne({'_id': Meteor.userId()}).household;
if(Meteor.userId() &&
Households.findOne({users : {$regex : new RegExp(Meteor.userId())}}))
{
console.log('Trying to get id');
return Households.findOne({users : {$regex : new RegExp(Meteor.userId())}})._id;
}
}
});
Template.body.events({
"submit .new-task": function (event) {
// This function is called when the new task form is submitted
var text = event.target.text.value;
text = text.trim();
if(text)//Check for non-null, non-empty
{
text = capitalizeEachWord(text);
console.log(text);
Meteor.call("addItem",text);
}
//Clear form
event.target.text.value = "";
//Prevent default form submit
return false;
},
"submit .new-household": function (event) {
// This function is called when the new task form is submitted
var insertedId;
var text = event.target.householdName.value;
text = text.trim();
if(text)//Check for non-null, non-empty
{
text = capitalizeEachWord(text);
Meteor.call("createHousehold", text);
}
//Prevent default form submit
return false;
}
});
Accounts.ui.config({
passwordSignupFields: "USERNAME_ONLY",
});
Code to subscribe to my collections located at 'client/application.js'
//Sub to our collections
Meteor.subscribe('households');
Meteor.subscribe('items');
Code iterating through the items to display located at 'client/templates/itemList.js'
Template.itemList.helpers({
items: function() {
console.log('Trying to subscribe');
return Items.find(
{
household : Households.findOne( {users : {$regex : new RegExp(this.userId)}})._id
}
, {sort : {checked: 1, createdAt: -1}});
},
householdId: function() {
//return Meteor.users.findOne({'_id': Meteor.userId()}).household;
if(Meteor.userId())
{
console.log('Trying to get id from ');
return Households.findOne({users : {$regex : new RegExp(Meteor.userId())}})._id;
}
}
});
Collection Code
Households collection code located at 'lib/collections/households.js'
Households = new Mongo.Collection("households");
Meteor.methods({
createHousehold: function(text) {
var insertedId;
insertedId = Households.insert({
householdName: text,
createdAt: new Date(), //current time
users: this.userId
});
}//Add user in the future
});
Items Collection code located at 'lib/collections/items.js'
Items = new Mongo.Collection("items");
Meteor.methods({
addItem: function (text) {
if (! Meteor.userId()){
throw new Meteor.Error("not-authorized");
}
console.log('Inserting item')
console.log('Item : ' + text);
console.log('UserId : ' + Meteor.userId());
console.log('Username : ' + Meteor.user().username);
console.log('Household : ' + Households.findOne( {users : {$regex : new RegExp(Meteor.userId())}})._id);
Items.insert({
text: text,
createdAt: new Date(), //current time
owner: Meteor.userId(),
username: Meteor.user().username,
household: Households.findOne( {users : {$regex : new RegExp(Meteor.userId())}})._id
});
return true;
},
deleteItem: function (itemId) {
Items.remove(itemId);
},
setChecked: function(itemId, setChecked) {
Items.update(itemId, { $set: {checked: setChecked}});
}
});
Server Code
The server code is located at 'server/main.js'
Meteor.publish("households", function(){
console.log('Trying to subscribe');
console.log(this.userId);
if(this.userId)
{
//var query = { users : new RegExp("/"+this.userId+"/")};
//console.log(Households.findOne( query ));
console.log(Households.findOne( {users : {$regex : new RegExp(this.userId)}}));
return Households.find( {users : {$regex : new RegExp(this.userId)}});
}
else
{
console.log('Too early');
return null;
}
});
Meteor.publish("items", function(){
console.log('Trying to get items');
if(!this.userId ||
!Households.findOne( {users : {$regex : new RegExp(this.userId)}}))
{
console.log('Returning null');
return null;
}
console.log(Items.findOne( {household : Households.findOne( {users : {$regex : new RegExp(this.userId)}}) }));
console.log(this.userId);
return Items.find( {household : Households.findOne( {users : {$regex : new RegExp(this.userId)}})._id });
});
Logs
If I start the site on localhost using meteor. When the page comes up (no user logged in), this the server console output:
=> App running at: http://localhost:3000/
I20141209-10:26:50.719(-5)? Trying to subscribe
I20141209-10:26:50.766(-5)? null
I20141209-10:26:50.766(-5)? Too early
I20141209-10:26:50.766(-5)? Trying to get items
I20141209-10:26:50.767(-5)? Returning null
Next I successfully create a user account (using accounts-ui/accounts-password packages). This is the output to that point.
I20141209-10:31:59.562(-5)? Trying to subscribe
I20141209-10:31:59.565(-5)? null
I20141209-10:31:59.566(-5)? Too early
I20141209-10:31:59.566(-5)? Trying to get items
I20141209-10:31:59.566(-5)? Returning null
I20141209-10:32:16.145(-5)? Trying to subscribe
I20141209-10:32:16.145(-5)? 8Skhof4jL2pSguT8Q
I20141209-10:32:16.146(-5)? undefined
I20141209-10:32:16.147(-5)? Trying to get items
I20141209-10:32:16.148(-5)? Returning null
Next I create a household using the .new-household form. No new output on the server at this point but here is the output on the client side:
main.js?ba2ac06f3ff7f471a7fa97093ac9ed5c01e0c8cd:7 Trying to get id
main.js?ba2ac06f3ff7f471a7fa97093ac9ed5c01e0c8cd:7 Trying to get id
itemList.js?a224bda493b90eb94bff9b88b48bb22eaa8aefe1:3 Trying to subscribe
main.js?ba2ac06f3ff7f471a7fa97093ac9ed5c01e0c8cd:7 Trying to get id
main.js?ba2ac06f3ff7f471a7fa97093ac9ed5c01e0c8cd:7 Trying to get id
itemList.js?a224bda493b90eb94bff9b88b48bb22eaa8aefe1:3 Trying to subscribe
At this point I add an item using the .new-task form. The task displays for a blink on screen then disappears. Here is the server output at this point:
I20141209-10:37:09.171(-5)? Inserting item
I20141209-10:37:09.172(-5)? Item : Beans
I20141209-10:37:09.172(-5)? UserId : 8Skhof4jL2pSguT8Q
I20141209-10:37:09.172(-5)? Username : pedro
I20141209-10:37:09.173(-5)? Household : M5NckT6ndqhRKCeWo
Here is the client output at this point:
main.js?ba2ac06f3ff7f471a7fa97093ac9ed5c01e0c8cd:21 Beans
items.js?2ca606d1e71e2e55d91421d37f060bd5d8db98fe:8 Inserting item
items.js?2ca606d1e71e2e55d91421d37f060bd5d8db98fe:9 Item : Beans
items.js?2ca606d1e71e2e55d91421d37f060bd5d8db98fe:10 UserId : 8Skhof4jL2pSguT8Q
items.js?2ca606d1e71e2e55d91421d37f060bd5d8db98fe:11 Username : pedro
items.js?2ca606d1e71e2e55d91421d37f060bd5d8db98fe:12 Household : M5NckT6ndqhRKCeWo
At this point the Item exists within the server database, but if I, using the web console in chrome, execute the command Items.findOne(); it returns undefined.
There could be more to it, but I couldn't see there where you have included the Meteor.subscribe("items") on the client side as that is what will pass the records to the client from the publish on the server.
Also in your publish, you can use
if (this.userId) {
//do stuff logged in
} else {
this.stop();
}
instead of selecting the Meteor.user() record for the user ID to detect if the client is logged in or not.
Your publication:
Meteor.publish("items", function(){
if(user = Meteor.user()){
return Items.find( {household : user.household});
}
else{
this.ready();
}
});
Your block helper:
Template.itemList.helpers({
items: function() {
return Items.find({}, {sort : {checked: 1, createdAt: -1}});
}
});
Your subscription (client side):
Meteor.subscribe("items");
If you are using iron:router. In the route you can define directly your helper and your subscription like this and just call {{items}} in your template:
Router.route('myRoute', function(){
this.render('myRouteTemplate');
}, {
path: '/myRoutePath',
waitOn: function(){
if(user = Meteor.user()) {
var subscriptions = [];
//
// Subscribe to your subscription
//===============================
subscriptions.push(Meteor.subscribe('items'));
return subscriptions;
}
},
data: function(){
var context;
if(this.ready()) {
if (user = Meteor.user()) {
context = {
items: Items.find( {household : user.household})
};
}
}
}
});
Always remember that when you publish a magazine, you need a subscriber process. Same thing for collections.
Cheers,
I ended up fixing this issue by implementing Iron Router in my application and waiting on subscriptions to be ready before rendering a template. I believe that my issue before was some kind of race condition but i'm not positive on the specifics of it. Either way i'm satisfied with my solution because it fixes my issue and structures the flow of my site better and more understandably I think.

Parse Cloud Code: Delete All Objects After Query

Scenario
I have an app that allows users to create an account, but also allows the user's the ability to delete their account. Upon deletion of their account I have a Cloud Code function that will delete all of the "Post"s the user has made. The cloud code I am using is...
//Delete all User's posts
Parse.Cloud.define("deletePosts", function(request, response) {
var userID = request.params.userID;
var query = new Parse.Query(Parse.Post);
query.equalTo("postedByID", userID);
query.find().then(function (users) {
//What do I do HERE to delete the posts?
users.save().then(function(user) {
response.success(user);
}, function(error) {
response.error(error)
});
}, function (error) {
response.error(error);
});
});
Question
Once I have the query made for all of the user's posts, how do I then delete them? (see: //What do I do HERE?)
You could use
Parse.Object.destroyAll(users); // As per your code – what you call users here are actually posts
See: http://parseplatform.org/Parse-SDK-JS/api/classes/Parse.Object.html#methods_destroyAll
Also, consider using Parse.Cloud.afterDelete on Parse.User (if that is what you mean by "deleting account") to do cleanups such as these.
Oh, and just to be complete, you don't need the save() routine after destroyAll()
Updates in-line below below your "What do I do HERE..." comment:
NOTES:
You don't need to call the save() method, so I took that out.
This, of course, is merely a matter of personal preference, but you may want to choose a parameter name that makes a little more sense than "users", since you're really not querying users, but rather Posts (that just happen to be related to a user).
Parse.Cloud.define("deletePosts", function(request, response) {
var userID = request.params.userID;
var query = new Parse.Query(Parse.Post);
query.equalTo("postedByID", userID);
query.find().then(function (users) {
//What do I do HERE to delete the posts?
users.forEach(function(user) {
user.destroy({
success: function() {
// SUCCESS CODE HERE, IF YOU WANT
},
error: function() {
// ERROR CODE HERE, IF YOU WANT
}
});
});
}, function (error) {
response.error(error);
});
});

Categories

Resources