Implement web notifications to meteor js app - meteor

Hello I'm trying to learn Meteor Js and i started building a simple task manager app.
I want to add a notification (using the notifications api), when a user adds a task from an input. My problem is that I cant find a way to send the notifications between users from the server side.
What i have so far :
Template.listPage.events({
'submit #newItem':function(e){
e.preventDefault();
//variables to insert here here. i dont post for space issues.
// Call server method for insert into db
Meteor.call('addItem',item,d,user,datestring,currentList,listName,assign);
// This is what i m trying to implement
var items = todos.find({name:item}).observe({
added:function() {
var n = new Notification("hello new task added")
}
})
},
The above works as :
The notification is shown only to the user that adds the task (not really useful).
How can I use this in server side, In order to:
Show the notification to all users using the app? Is this possible or I have to use a push notification package?
PS: I ask for users permition to receive notifications on Meteor.startup.

The problem is that you specify the observe within the click event. Therefore, only users who have triggered the event (i.e. clicked the button) will get the notification.
In order to activate the observer for everyone do the following:
Template.listPage.rendered = function(){
todos.find().observe({
added: function() {
... do whatever you want to do here
}
})
};

To add notifications, you could use a package such as yogiben:notifications:
https://atmospherejs.com/yogiben/notifications
(There might be better packages for this nowadays. Search on Atmosphere.)
Alternatively, you can roll your own solution. The basic idea would be add create a collection called Notifications. Then add notifications whenever you feel like it and publish the relevant notifications to whichever user(s) you'd like.
The code in your question will also work as long as you're publishing the relevant todos to different users.

Related

I can't get "Find personal time entry in progress" using clockify API

I will try to get those users whose time tracking start currently in clockify
and I'm trying to use the following API endpoint to get the user:
How to get user list using this endpoint?
I have made custom logic for check user is currently working or not
I have made a loop that checks the user is in progress or not.
e.g
public getClockifyBaseWorkerStatus(clokifyApiKey: string) {
let headers = new HttpHeaders().set('X-Api-Key', clokifyApiKey ? clokifyApiKey : environment.clokifyApiKey);
return this.http.get<any>(`https://api.clockify.me/api/workspaces/${environment.clockifyWorkSpace}/timeEntries/inProgress`, { headers })
.pipe(map((data) => { return data; }));
}
so, in this function, I have pass different clokifyApiKey and check the status of the user is currently working or not.
I hope this answer will help other people in the future.
The GET /workspaces/{workspaceId}/timeEntries/inProgress only returns your own currently running timer, not the entire workspaces, per the API docs (https://clockify.github.io/clockify_api_docs/#tag-Time-entry). So you can't get a list of active timers in the workspace other than for yourself. From what I can tell it is not possible to get a list of users with active timers.

Using onResetPasswordLink, onEnrollmentLink, and onEmailVerificationLink methods properly in Meteor

I was wondering if someone would be kind enough to provide a meteorpad or code example of using one of the methods listed above properly in Meteor (with iron:router). I'm struggling to understand how exactly these methods interact with my app, and it seems these methods are new enough that there isn't much good documentation on how to use them correctly. Thanks!
http://docs.meteor.com/#/full/Accounts-onResetPasswordLink
Ok, so I am going to post what I ended up learning and doing here so others can use it as a reference. I'll do my best to explain what is happening as well.
As can be seen in the other comments, the 'done' function passed to the Accounts.on****Link callback was the main part that tripped me up. This function only does one thing - re-enables autoLogin. It's worth noting that the 'done' function/autoLogin is a part of one of the core 'accounts' packages, and cannot be modified. 'autoLogin' is used in one particular situation: User A tries to reset his or her pw on a computer where User B is currently logged in. If User A exits the reset password flow before submitting a new password, then User B will remain logged in. If User A completes the reset password flow, then User B is logged out and User A is logged in.
The pattern used to handle 'done' in the accounts-ui package, and what I ended up doing, assigns 'done' to a variable that can then be passed to your template event handler function, and run once your reset password logic is complete. This variable assignment needs to be done in the Accounts.on****Link callback, but the callback can be placed in any top-level client side code (just make sure you assign the scope of the variables correctly). I just put it at the start of my reset_password_template.js file (I've only done this for resetting passwords so far, but the pattern should be similar):
client/reset_password_template.js:
// set done as a variable to pass
var doneCallback;
Accounts.onResetPasswordLink(function(token, done) {
Session.set('resetPasswordToken', token); // pull token and place in a session variable, so it can be accessed later
doneCallback = done; // Assigning to variable
});
The other challenge of using these on****Link callbacks is understanding how your app 'knows' the callback has been fired, and what needs to be done by the app. Since iron:router is so tightly integrated with Meteor, it's easy to forget it is a separate package. It's important to keep in mind these callbacks were written to operate independently of iron:router. This means when the link sent to your email is clicked, your app is loaded at the root level ('/').
***Side note - There are some other answers here on StackOverflow that offer ways to integrate with iron:router, and load a specific route for each link. The problem for me with these patterns was that they seemed a bit hackish, and not in line with the 'meteor' way. More importantly, if the core Meteor team decides to alter the path of these registration links, these routes would break. I tried calling Router.go('path'); in the on****Link callback, but for some reason this didn't work in Chrome and Safari. I would love to have a way to handle specific routes for each of these emailed links, thus eliminating the need for constantly setting and clearing Session variables, but I couldn't think of a good solution that worked.
Anyways, as #stubailo described in his answer, your app is loaded (at the root level), and the callback is fired. Once the callback is fired, you have your session variable set. You can use this session variable to load the appropriate templates at the root level using the following pattern:
client/home.html (or your landing page template)
{{#unless resetPasswordToken}}
{{> home_template}}
{{else}}
{{> reset_password_template}}
{{/unless}}
With this, there are few things you need to take care of in your reset_password_template.js file, and home.js:
client/home.js
// checks if the 'resetPasswordToken' session variable is set and returns helper to home template
Template.home.helpers({
resetPasswordToken: function() {
return Session.get('resetPasswordToken');
}
});
client/reset_password_template.js
// if you have links in your template that navigate to other parts of your app, you need to reset your session variable before navigating away, you also need to call the doneCallback to re-enable autoLogin
Template.reset_password_template.rendered = function() {
var sessionReset = function() {
Session.set('resetPasswordToken', '');
if (doneCallback) {
doneCallback();
}
}
$("#link-1").click(function() {
sessionReset();
});
$('#link2').click(function() {
sessionReset();
});
}
Template.reset_password_template.events({
'submit #reset-password-form': function(e) {
e.preventDefault();
var new_password = $(e.target).find('#new-password').val(), confirm_password = $(e.target).find('#confirm-password').val();
// Validate passwords
if (isNotEmpty(new_password) && areValidPasswords(new_password, confirm_password)) {
Accounts.resetPassword(Session.get('resetPasswordToken'), new_password, function(error) {
if (error) {
if (error.message === 'Token expired [403]') {
Session.set('alert', 'Sorry, this link has expired.');
} else {
Session.set('alert', 'Sorry, there was a problem resetting your password.');
}
} else {
Session.set('alert', 'Your password has been changed.'); // This doesn't show. Display on next page
Session.set('resetPasswordToken', '');
// Call done before navigating away from here
if (doneCallback) {
doneCallback();
}
Router.go('web-app');
}
});
}
return false;
}
});
Hopefully this is helpful for others who are trying to build their own custom auth forms. The packages mentioned in the other answers are great for many cases, but sometimes you need additional customization that isn't available via a package.
I wrote this method, so hopefully I can give a good example of how to use it.
It's meant to be in conjunction with Accounts.sendResetPasswordEmail and Accounts.resetPassword (http://docs.meteor.com/#/full/accounts_sendresetpasswordemail and http://docs.meteor.com/#/full/accounts_resetpassword).
Basically, let's say you want to implement your own accounts UI system instead of using the accounts-ui package or similar. If you want to have a password reset system, you need three things:
A way to send an email with a password reset link
A way to know when the user has clicked the reset link
A method to actually reset the password
Here is how the flow should work:
The user clicks a link on your page that says "Reset password"
You find out which user that is (possibly by having them enter their email address), and call Accounts.sendResetPasswordEmail
The user clicks the reset password link in the email they just received
Your app is loaded and registers a callback with Accounts.onResetPasswordLink
The callback is called because the URL has a special fragment in it with the password reset token
This callback can display a special UI element that asks the user to input their new password
The app calls Accounts.resetPassword with the token and the new password
Now the user is logged in and they have a new password
This is a little complicated because it is the most advanced and custom flow possible. If you don't want to mess around with all of these callbacks and methods, I would recommend using one of the existing accounts UI packages, for example accounts-ui or https://atmospherejs.com/ian/accounts-ui-bootstrap-3
For some example code, take a look at the code for the accounts-ui package: https://github.com/meteor/meteor/blob/devel/packages/accounts-ui-unstyled/login_buttons_dialogs.js
Per the documentation:
You can construct your own user interface using the functions below, or use the accounts-ui package to include a turn-key user interface for password-based sign-in.
Therefore, those callback are for rolling your own custom solution. However, I would recommend using one of the following packages below, with accounts-entry being my preferred solution:
Use a combination of accounts-password and accounts-ui
Or use https://atmospherejs.com/joshowens/accounts-entry, especially if you want OAuth integrations such as Facebook, Twitter, etc. For handling email verification with this package, please see this Github issue.
It's been a year since this question but I just came up with the same problem.
Following your solution, what I found is that you could use the Session variable within the router and the onAfterAction hook to achieve the same, but using routes:
Router.route('/', {
name: 'homepage',
action: function() {
if (Session.get('resetPasswordToken')) {
this.redirect('resetPassword', {token: Session.get('resetPasswordToken')});
} else {
this.render('home');
}
}
});
Router.route('/password/reset/:token', {
name: 'resetPassword',
action: function () {
this.render('resetPassword');
},
data: function() {
return {token: this.params.token};
},
onAfterAction: function () {
Session.set('resetPasswordToken', '');
}
});
Of course, you will need also:
Accounts.onResetPasswordLink(function(token, done){
Session.set('resetPasswordToken', token);
doneResetPassword = done;
});

Meteor, get all users on a specific page

We are building a chat application and are currently working on a system to see all the users in a given room.
We have a Mongo Document set up with an array of active_users where we will push and pull user names to in order to keep track of the online users. We have come to the conclusion that realizing a user has connected to a given room is fairly simple. All we need to do is in the router, when a user accesses the page, we push that user's name into the document.
Now the tricky part is realizing when that user has left that given page? Obviously jQuery isn't a reliable option, so how do we know when a user's connection to a specific page is broken?
You could do this:
Meteor.publish("page", function() {
this._session.socket.on("close", function() {
//Change your active users here
});
});
and for your page that you track
Meteor.subscribe('page');
I use this in the analytics package on atmosphere
There's an Atmosphere package called Presence that does exactly what you need.
Some extra details from the README about keeping track of custom states...
State functions
If you want to track more than just users' online state, you can set a custom state function. (The default state function returns just 'online'):
// Setup the state function on the client
Presence.state = function() {
return {
online: true,
currentRoomId: Session.get('currentRoomId')
};
}
Now we can simply query the collection to find all other users that share the same currentRoomId
Presences.find({ state: { online: true, currentRoomId: Session.get('currentRoomId') } })
Of course, presence will call your function reactively, so everyone will know as soon as things change.
Meteor has connection hooks so you can run a function when the user disconnects from the server. Setting the onClose() callback inside a method called by the client will allow you to close the userId in the function.
Code on the server could be like this:
Meteor.methods({
joinRoom: function( roomId ){
var self = this;
Rooms.update( {roomId: roomId}, {$push:{userId: self.userId}});
self.connection.onClose( function(){
Rooms.update( {roomId: roomId}, {$pull:{userId: self.userId}})
});
}
});

subscribe based on date in meteor

I'm kind of new to web development and I became a fan of meteor because of the way it lets me do cool stuf really easy. I have been toying around with the parties example and I have added a date attribute for the parties. I would like to only subscribe the client to parties that have not yet expired.
Essentially where datenow < partydate.
I find myself stuck in writing the correct subscribe code as I only find documentation on how to subscribe based on database attributes and not based on comparing the date of the party with the current date.
Meteor.subscribe("parties"); --> I think this is the part of the code on the client that I need to edit.
I really hope somebody could show me in the right direction on writing the correct subscribe code.
The client subscribes to what the server is willing to send to them.
if(Meteor.isClient){
Meteor.subscribe("parties");
}
The server filters data the client shouldn't have, typically for security reasons. You wouldn't want passwords or private information being published. Any client can open up the console and browse the full data set that was published to them.
if(Meteor.isServer){
Meteor.publish("parties", function(){
return Parties.find({date: {$gt: Date.now()}});
});
}
If you want clients to be able to see both expired parties and non-expired parties, you would publish the whole set from the server, then filter it on the client in a template helper.
if(Meteor.isServer){
Meteor.publish("parties", function(){
return Parties.find();
});
}
if(Meteor.isClient){
Meteor.subscribe("parties");
Template.templateName.allParties = function(){
return Parties.find();
}
Template.templateName.activeParties = function(){
return Parties.find({date: {$gt: Date.now()}});
}
}

How to know when user document loaded in Meteor Accounts

I understand that when writing code that depends on the collection being loaded into the client minimongo, that you should explicitly subscribe to the collection and pass in the appropriate callback for when it is finished loading.
My problem is that I store a lot of important subdocuments that my page needs to access in the users collection. I am using Meteor Accounts, and am trying to figure out a similar way to wait until the entire logged in user document is available. When using this to test:
console.log(Meteor.user());
the logged in case, it seems like it first registers an object with just the _id, and then sends the other fields later (I know I have to explicitly add other fields to publish from the server beyond email, etc.).
Is there a way for me to wait for the logged in user document to load completely before executing my code?
Thanks!
Deps.autorun (previously Meteor.autorun) reruns when something reactive changes, which might fit your use case:
Client js
Deps.autorun(function () {
if(Meteor.user() {
//Collection available
}
});
If you're using a subscription you can also use its callback. Have a read about it on the docs as you might have to customize it a bit, and remove the autopublish package as well as get your other collections set up to subscriptions
Server js:
Meteor.publish("userdata", function () {
//You might want to alter this depending on what you want to send down
return Meteor.users.find({}, {}});
});
Client js
Meteor.subscribe("userdata", function() {
//Collection available
});

Resources