Meteor Notifications

2022-06-30 13:59 更新

Notifications

現(xiàn)在用戶們可以給帖子添加評(píng)論了,讓他們互相知道討論已經(jīng)開始了是個(gè)好主意。

我們將通知帖子的作者已經(jīng)有用戶在他的帖子上添加了評(píng)論,并且提供一個(gè)鏈接可以看到評(píng)論。

這是 Meteor 真正閃光的特性之一:因?yàn)?Meteor 在默認(rèn)情況下是實(shí)時(shí)的,我們會(huì)_瞬時(shí)_看到這些 notifications。不需要用戶去刷新頁面或者做其他檢查,我們不需要寫任何特殊代碼就可以得到一個(gè)簡單的 notifications 彈出框。

生成 Notifications

當(dāng)有人在你的帖子上添加評(píng)論時(shí)我們將生成一個(gè) notification。在后面,notifications 會(huì)擴(kuò)展覆蓋很多其他情況,但是目前讓用戶知道正在發(fā)生什么就足夠了。

讓我們先創(chuàng)建一個(gè) Notifications 集合,和一個(gè)方法 createCommentNotification,當(dāng)有人在你的帖子下添加評(píng)論時(shí)該方法會(huì)添加一個(gè) notification 到集合。

因?yàn)槲覀儗目蛻舳烁?notifications, 我們需要確定 allow 方法是防彈的。我們檢查如下內(nèi)容:

  • 用戶是文檔的創(chuàng)建者才能調(diào)用 update 方法
  • 用戶只更新一個(gè)屬性
  • 更新的屬性名字是 read
Notifications = new Mongo.Collection('notifications');

Notifications.allow({
  update: function(userId, doc, fieldNames) {
    return ownsDocument(userId, doc) &&
      fieldNames.length === 1 && fieldNames[0] === 'read';
  }
});

createCommentNotification = function(comment) {
  var post = Posts.findOne(comment.postId);
  if (comment.userId !== post.userId) {
    Notifications.insert({
      userId: post.userId,
      postId: post._id,
      commentId: comment._id,
      commenterName: comment.author,
      read: false
    });
  }
};

和帖子和評(píng)論一樣,Notifications 集合也是在服務(wù)器端和客戶端共享的。用戶看完 notifications 后,我們需要更新他們,因此需要允許更新操作。和其他部分一樣只有擁有 notification 的用戶才允許更新操作。

我們寫了個(gè)簡單的程序用來當(dāng)用戶給帖子添加評(píng)論時(shí)找到需要通知的用戶,并插入一個(gè)新的 notification。

我們已經(jīng)在服務(wù)器端方法創(chuàng)建了評(píng)論對(duì)象,我們用 comment._id = Comments.insert(comment) 來替換 return Comments.insert(comment);。這樣就能在評(píng)論對(duì)象中保存 _id, 然后調(diào)用 createCommentNotification 方法:

Comments = new Mongo.Collection('comments');

Meteor.methods({
  commentInsert: function(commentAttributes) {

    //...

    comment = _.extend(commentAttributes, {
      userId: user._id,
      author: user.username,
      submitted: new Date()
    });

    // update the post with the number of comments
    Posts.update(comment.postId, {$inc: {commentsCount: 1}});

    // create the comment, save the id
    comment._id = Comments.insert(comment);

    // now create a notification, informing the user that there's been a comment
    createCommentNotification(comment);

    return comment._id;
  }
});

接下來發(fā)布 notifications:

Meteor.publish('posts', function() {
  return Posts.find();
});

Meteor.publish('comments', function(postId) {
  check(postId, String);
  return Comments.find({postId: postId});
});

Meteor.publish('notifications', function() {
  return Notifications.find();
});

在客戶端訂閱 notifications:

Router.configure({
  layoutTemplate: 'layout',
  loadingTemplate: 'loading',
  notFoundTemplate: 'notFound',
  waitOn: function() {
    return [Meteor.subscribe('posts'), Meteor.subscribe('notifications')]
  }
});

顯示 Notifications

現(xiàn)在我們在 header 中添加一個(gè) notifications 列表。

<template name="header">
  <nav class="navbar navbar-default" role="navigation">
    <div class="container-fluid">
      <div class="navbar-header">
        <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navigation">
          <span class="sr-only">Toggle navigation</span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
        </button>
        <a class="navbar-brand" href="{{pathFor 'postsList'}}">Microscope</a>
      </div>
      <div class="collapse navbar-collapse" id="navigation">
        <ul class="nav navbar-nav">
          {{#if currentUser}}
            <li>
              <a href="{{pathFor 'postSubmit'}}">Submit Post</a>
            </li>
            <li class="dropdown">
              {{> notifications}}
            </li>
          {{/if}}
        </ul>
        <ul class="nav navbar-nav navbar-right">
          {{> loginButtons}}
        </ul>
      </div>
    </div>
  </nav>
</template>

接下來創(chuàng)建 notificationsnotificationItem 模板 (兩個(gè)模板都在 notifications.html 文件中):

<template name="notifications">
  <a href="#" class="dropdown-toggle" data-toggle="dropdown">
    Notifications
    {{#if notificationCount}}
      <span class="badge badge-inverse">{{notificationCount}}</span>
    {{/if}}
    <b class="caret"></b>
  </a>
  <ul class="notification dropdown-menu">
    {{#if notificationCount}}
      {{#each notifications}}
        {{> notificationItem}}
      {{/each}}
    {{else}}
      <li><span>No Notifications</span></li>
    {{/if}}
  </ul>
</template>

<template name="notificationItem">
  <li>
    <a href="{{notificationPostPath}}">
      <strong>{{commenterName}}</strong> commented on your post
    </a>
  </li>
</template>

我們可以看到每個(gè) notification 有一個(gè)指向帖子的鏈接,而且包含注釋作者的名字。

接下來我們需要確定在 helper 中選擇了正確的 notifications 列表,并且在用戶點(diǎn)擊鏈接后將 notifications 標(biāo)記為已讀。

Template.notifications.helpers({
  notifications: function() {
    return Notifications.find({userId: Meteor.userId(), read: false});
  },
  notificationCount: function(){
    return Notifications.find({userId: Meteor.userId(), read: false}).count();
  }
});

Template.notificationItem.helpers({
  notificationPostPath: function() {
    return Router.routes.postPage.path({_id: this.postId});
  }
});

Template.notificationItem.events({
  'click a': function() {
    Notifications.update(this._id, {$set: {read: true}});
  }
});

你可能覺得 notifications 和 errors 很像,是的從結(jié)構(gòu)上看他們很相似。但有一點(diǎn)不同: 我們?yōu)?notification 創(chuàng)建了一個(gè)集合。這意味著 notification 是持久化的,他對(duì)于同一用戶不論瀏覽器刷新還是跨設(shè)備都是存在的。

試一下: 打開一個(gè)新的瀏覽器 (比如 Firefox), 然后創(chuàng)建一個(gè)新用戶, 然后在主用戶的帖子下發(fā)表一個(gè)評(píng)論 (在 Chrome 中)。你將看到如下:

控制 notifications 的訪問權(quán)限

Notifications 工作的很好。然后這有一個(gè)小問題:所有人都能看到我們的 notifications。

如果你的瀏覽器還開著,試一下在瀏覽器 console 中輸入以下 js 代碼:

? Notifications.find().count();
1

一個(gè)新的用戶 (當(dāng)有帖子被評(píng)論時(shí)) 不該收到任何 notifications. Notifications 集合中的內(nèi)容實(shí)際上屬于以前的用戶。

除了可能的隱私問題外,我們不能讓瀏覽器顯示每條 notifications 的原因是. 對(duì)于一個(gè)足夠規(guī)模的網(wǎng)站,這么做會(huì)很容易耗盡瀏覽器內(nèi)存,并帶來嚴(yán)重的性能問題。

我們通過 publications 來解決這個(gè)問題。我們可以通過 publications 來精確的指定哪部分集合我們想共享給其他用戶。

為了實(shí)現(xiàn)這個(gè),我們需要在 publication 中返回不同的 cursor 而不是使用 Notifications.find()。換句話說,我們返回的 cursor 是和當(dāng)前用戶的 notificatons 相關(guān)的。

這么做足夠直接,因?yàn)?publish 函數(shù)有當(dāng)前用戶的 _id, 它存在于 this.userId 中:

Meteor.publish('notifications', function() {
  return Notifications.find({userId: this.userId, read: false});
});

現(xiàn)在看一下我們的兩個(gè)瀏覽器窗口,我們會(huì)看到兩個(gè)不同的 notifications 集合。

? Notifications.find().count();
1
? Notifications.find().count();
0

實(shí)際上,當(dāng)你登錄和登出時(shí) Notifications 集合都會(huì)變化。這是因?yàn)楫?dāng)賬戶變化時(shí) publications 會(huì)自動(dòng)重新發(fā)布。

我們的 app 功能越來越完善,當(dāng)越來越多的用戶發(fā)帖時(shí),我們的首頁會(huì)無限長。下一章我們通過分頁來解決這個(gè)問題。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)