Powiadomienia

11

procent przetłumaczone

W tym rozdziale dowiesz się:

  • Jak dodać kolekcję powiadomienia aby powiadamiać użytkowników o działaniach innych użytkowników.
  • Jak udostępnić powiadomienia pożądane wyłącznie dla danego użytkownia.
  • Naucz się więcej o publikacjach i subskrypcjach.
  • Teraz kiedy użykownicy mogą komentować na posty innych użytkowników, byłoby dobrze móc powiadamiać o rozpoczętej konwersacji.

    Aby tego dokonać, damy znać właścicielowi posta, że dodano komentarz i dostarczymy mu link, aby mógł go przeczytać.

    Jest to funkcjonalność, w której Meteor naprawdę błyszczy: ponieważ wszystko w Meteorze domyślnie dzieje się w czasie rzeczywistym, będziemy wyświetlali te notyfikacje natychmiastowo. Nie chcemy czekać na użytownika, aby odświeżył stronę czy sprawdzał to w jakikolwiek sposób, możemy po prostu pokazać nową notyfikację bez potrzeby pisania żadnego dodatkowego kodu.

    Tworzenie powiadomień

    Utworzymy powiadomienie gdy ktoś skomentuje Twój post. W przyszłości powiadomienia mogą być rozszerzone, aby wziąć pod uwagę wiele innych scenariuszy, ale teraz wystarczy to aby na bieżąco informować użytkowników o tym, co się dzieje.

    Stwórzmy kolekcję Notifications, oraz funkcję createCommentNotification, która wstawi pasujące powiadomienie do jednego z Twoich postów:

    Notifications = new Meteor.Collection('notifications');
    
    Notifications.allow({
      update: ownsDocument
    });
    
    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
        });
      }
    };
    
    collections/notifications.js

    Tak jak posty lub komentarze, kolekcja Notifications będzie współdzielona po stronie klienta i serwera. Ponieważ potrzebujemy uaktualniać powiadomienia po tym, kiedy użytkownik je zobaczył, włączamy również uaktualnienia, jak zwykle upewniając się, że ograniczamy uprawnienia do uaktualnień własnych danych użytkownika.

    Utworzyliśmy także prostą funkcję, która sprawdza post który jest aktualnie komentowany i buduje listę użytkowników, którzy mają być powiadomieni i wstawia nową notyfikację.

    Już tworzymy komentarze po stronie serwera, zatem możemy rozwinąć funkcję je tworzącą, aby wywoływała naszą funkcję. Zamienimy return Comments.insert(comment); przez comment._id = Comments.insert(comment) aby zaoszczędzić _id nowo utworzonego komentarza w zmiennej, a następnie zawołać naszą funkcję createCommentNotification:

    Comments = new Meteor.Collection('comments');
    
    Meteor.methods({
      comment: function(commentAttributes) {
    
        // [...]
    
        // utwórz komentarz, zachowaj jego id
        comment._id = Comments.insert(comment);
    
        // teraz utwórz powiadomienie informujące użytkownika o nowym komentarzu
        createCommentNotification(comment);
    
        return comment._id;
      }
    });
    
    collections/comments.js

    Opublikujemy także powiadomienia i utworzymy dla nich subskrypcje po stronie klienta.

    // [...]
    
    Meteor.publish('notifications', function() {
      return Notifications.find();
    });
    
    server/publications.js
    Router.configure({
      layoutTemplate: 'layout',
      loadingTemplate: 'loading',
      waitOn: function() { 
        return [Meteor.subscribe('posts'), Meteor.subscribe('notifications')]
      }
    });
    
    lib/router.js

    Zatwierdź 11-1

    Added basic notifications collection.

    Wyświetlanie powiadomień

    Możemy teraz kontynuować i dodać listę powiadomień do nagłówka.

    <template name="header">
      <header class="navbar">
        <div class="navbar-inner">
          <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </a>
          <a class="brand" href="{{pathFor 'postsList'}}">Microscope</a>
          <div class="nav-collapse collapse">
            <ul class="nav">
              {{#if currentUser}}
                <li>
                  <a href="{{pathFor 'postSubmit'}}">Submit Post</a>
                </li>
                <li class="dropdown">
                  {{> notifications}}
                </li>
              {{/if}}
            </ul>
            <ul class="nav pull-right">
              <li>{{loginButtons}}</li>
            </ul>
          </div>
        </div>
      </header>
    </template>
    
    client/views/includes/header.html

    I utworzyć szablony notifications i notification (będą dzieliły wspólny plik 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}}
            {{> notification}}
          {{/each}}
        {{else}}
          <li><span>No Notifications</span></li>
        {{/if}}
      </ul>
    </template>
    
    <template name="notification">
      <li>
        <a href="{{notificationPostPath}}">
          <strong>{{commenterName}}</strong> commented on your post
        </a>
      </li>
    </template>
    
    client/views/notifications/notifications.html

    Jak widać, zaplanowaliśmy, że każde powiadomienie ma zawierać link do posta, do którego dodano komentarz i nazwę użytkownika, który dodał komentarz.

    Następnie, musimy się upewnić, że zaznaczyliśmy prawidłową listę powiadomień w naszym managerze i uaktualnić powiadomienie jako czytaj gdy użytkownik kliknie na link, na który wzkazują.

    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.notification.helpers({
      notificationPostPath: function() {
        return Router.routes.postPage.path({_id: this.postId});
      }
    })
    
    Template.notification.events({
      'click a': function() {
        Notifications.update(this._id, {$set: {read: true}});
      }
    })
    
    client/views/notifications/notifications.js

    Zatwierdź 11-2

    Display notifications in the header.

    Możesz myśleć, że powiadomienia nie różnią się zbytnio od błedów i masz rację. Ich struktura jest podobna. Jest jednak jedna duża różnica między nimi: utworzyliśmy odpowiednią synchronizowaną kolekcję miedzy klientem i serwerem. Oznacza to, że nasze powiadomienia są zapamiętywane i tak długo jak używamy tego samego konta, będzie istniało po odświeżeniach przeglądarki i między różnymi urządzeniami, które przeglądają stronę.

    Wypróbuj to: otwórz drugą przeglądarkę (powiedzmy Firefox), utwórz nowe konto użytkownika, i skomentuj posta, którego utworzyłeś za pomocą głównego konta (które zostawiłeś otwarte w Chrome). Powinieneś zobaczyć mniej więcej to:

    Displaying notifications.
    Displaying notifications.

    Kontrola dostępu do powiadomień

    Powiadomienia działają dobrze. Jest jednak jeden problem: nasze powiadomienia są dostępne dla wszystkich.

    Jeżeli masz nadal otwartą drugą przeglądarkę, spróbuj uruchomić następujący kod w konsoli przeglądarki:

     Notifications.find().count();
    1
    
    Browser console

    Nowy użytkownik (ten, który skomentował) nie powinien otrzymać żadnego powiadomienia. Powiadomienie, które widzą w kolekcji Notifications należy do niego.

    Poza potencjalnymi problemami związanymi z zachowaniem prywatności, nie stać nas na to, aby załadować wszystkie powiadomienia w każdej przeglądarce innych użytkowników. Na wystarczająco dużym serwisie, mogłoby to zapełnić dostępną pamięć przeglądarki i spowodować poważne problemy związane ze spadkiem wydajności.

    Rozwiązaliśmy ten problem za pomocą publikacji. Możemy użyć publikacji aby bardzo szczegółowo określić, którą część kolekcji chcemy dzielić z każdą przeglądarką.

    Aby to osiągnąć, musimy zwrócić inny kursor niż Notifications.find() w naszej publikacji. Mianowicie, chcemy zwrócić kursor, która odpowiada bieżącemu powiadomieniu użytkownika.

    Osiągnięcie tego jest proste, ponieważ funkcja publish ma dostęp do _id bieżącego użytkownika dostępną jako this.userId:

    Meteor.publish('notifications', function() {
      return Notifications.find({userId: this.userId});
    });
    
    server/publications.js

    Zatwierdź 11-3

    Synchronizowanie wyłącznie tych powiadomień, które są prz…

    Teraz, gdy sprawdzimy dwie niezależnie uruchomione przeglądarki, powinniśmy zobaczyć dwie różne kolekcje powiadomień:

     Notifications.find().count();
    1
    
    Browser console (user 1)
     Notifications.find().count();
    0
    
    Browser console (user 2)

    Tak naprawdę, lista publikacji powinna zmieniać się nawet po wylogowaniu i ponownym zalogowaniu się do aplikacji. Dzieje się tak dlatego, ponieważ publikacje po każdej zmianie konta użytkownika są publikowane ponownie.

    Nasza aplikacja staje się coraz bardziej funkcjonalna i im więcej użytkowników przyłącza się i zaczyna publikować linki, tym większe ryzyko nieskończonie ładującej się stronie głównej. Zajmiemy się tym w następnym rozdziale przez zaimplementowanie dzielenia dokumentu na strony.