Fork me on GitHub

WhatsApp Clone with Meteor CLI and Ionic

Privacy

Note: If you skipped ahead to this section, click here to download a zip of the tutorial at this point.

Right now all the chats are published to all the clients which is not very safe for privacy. Let's fix that.

First thing we need to do in order to stop all the automatic publication of information is to remove the autopublish package from the Meteor server. Type in the command line:

$ meteor remove autopublish

We will add now the publish-composite package, which we will use later.

$ meteor add reywood:publish-composite

Now we need to explicitly define our publications. Let's start by sending the users' information.

Create a file named publications.js under the server folder and define the query we want to send to our clients:

6.3 Add users publication server/publications.js
1
2
3
4
5
import { Meteor } from 'meteor/meteor';
 
Meteor.publish('users', function() {
  return Meteor.users.find({}, { fields: { profile: 1 } });
});

And of course we need to modify some of the client side code, we need to make sure that the client is subscribed to the published data, so let's do so in NewChatCtrl, because this is where we need the users data:

6
7
8
9
10
11
12
13
  constructor() {
    super(...arguments);
 
    this.subscribe('users');
 
    this.helpers({
      users() {
        return Meteor.users.find({ _id: { $ne: this.currentUserId } });

Now let's do a more complex publication, let's send each client only his chats with their messages.

In order to do that, we need to do a joined collections publication. reywood:publish-composite package helps us achieve it with a very easy and convenient way.

6.5 Add chats publication server/publications.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { Meteor } from 'meteor/meteor';
import { Chats, Messages } from '../lib/collections';
 
Meteor.publish('users', function() {
  return Meteor.users.find({}, { fields: { profile: 1 } });
});
 
Meteor.publishComposite('chats', function() {
  if (!this.userId) return;
 
  return {
    find() {
      return Chats.find({ userIds: this.userId });
    },
    children: [
      {
        find(chat) {
          return Messages.find({ chatId: chat._id });
        }
      },
      {
        find(chat) {
          const query = { _id: { $in: chat.userIds } };
          const options = { fields: { profile: 1 } };
 
          return Meteor.users.find(query, options);
        }
      }
    ]
  };
});

Now we will add a subscription to the chats data in the client:

6.6 Add chats subscription client/scripts/routes.js
1
2
3
4
5
 
24
25
26
27
28
29
30
31
32
33
import { _ } from 'meteor/underscore';
import { Meteor } from 'meteor/meteor';
import { Config, Runner } from 'angular-ecmascript/module-helpers';
 
import chatsTemplateUrl from '../templates/chats.html';
...some lines skipped...
        abstract: true,
        templateUrl: tabsTemplateUrl,
        resolve: {
          user: this.isAuthorized,
          chats() {
            return Meteor.subscribe('chats');
          }
        }
      })
      .state('tab.chats', {