forked from DiscoverMeteor/DiscoverMeteor_Pt
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path11-notifications.md.erb
247 lines (193 loc) · 9.58 KB
/
11-notifications.md.erb
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
---
title: Notificações
slug: notifications
date: 0011/01/01
number: 11
contents: Adicione uma coleção de notificações para notificar usuários sobre ações de outros usuários.| Aprenda como apresentar apenas notificações relevantes para um determinado usuário.|Aprenda mais sobre publicações e subscrições no Meteor.
paragraphs: 25
---
Agora que os usuários possam comentar nos artigos uns dos outros, seria bom permití-los saber que uma conversa começou.
Para tanto, nós notificaremos o dono do artigo de que houve um comentário em seu artigo, e providenciaremos um link para eles verem esse comentário.
Este é o tipo de utilidade onde o Meteor realmente brilha: por o Meteor ser em tempo real por padrão, nós mostraremos essas notificações _instantaneamente_. Nós não precisamos esperar o usuário atualizar a página ou checar manualmente, nós podemos simplesmente pipocar novas notificações sem precisar escrever nenhum código especial.
### Criando notificações
Nós criaremos uma notificação quando alguém comenta nos seus artigos. No futuro, notificações poderão ser estendidas para cobrir muitos outros cenários, mas por hora isso seria o suficiente para manter os usuários informados do que está acontecendo.
Vamos criar nossa coleção `Notifications`, assim como uma função `createCommentNotification` que inserirá uma notificação correspondente a cada novo comentário em um de seus artigos:
~~~js
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
});
}
};
~~~
<%= caption "collections/notifications.js" %>
Assim como artigos e comentários, esta coleção `Notifications` será compartilhada tanto pelo cliente quanto pelo servidor. Já que nós precisamos atualizar as notificações uma vez que o usuário já as viu, nós também disponibilizamos atualizações, garantindo como sempre que nós restrinjamos as permissões de atualização à própria informação do usuário.
Nós também criamos uma simples função que olha para o artigo que o usuário está comentando, descobre quem deve ser notificado a partir daí, e insere uma nova notificação.
Nós já estamos criando comentários com um Método do lado do servidor, então nós podemos apenas melhorar este Método para chamar a nossa função. Nós trocaremos `return Comments.insert(comment);` por `comment._id = Comments.insert(comment)` para salvar a `_id` do recém criado comentário em uma variável, então chamamos nossa função `createCommentNotification`:
~~~js
Comments = new Meteor.Collection('comments');
Meteor.methods({
comment: function(commentAttributes) {
// [...]
// 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;
}
});
~~~
<%= caption "collections/comments.js" %>
<%= highlight "8~14" %>
Vamos também publicar as notificações, e fazer assinatura no cliente:
~~~js
// [...]
Meteor.publish('notifications', function() {
return Notifications.find();
});
~~~
<%= caption "server/publications.js" %>
~~~js
Router.configure({
layoutTemplate: 'layout',
loadingTemplate: 'loading',
waitOn: function() {
return [Meteor.subscribe('posts'), Meteor.subscribe('notifications')]
}
});
~~~
<%= caption "lib/router.js" %>
<%= highlight "5" %>
<%= commit "11-1", "Added basic notifications collection." %>
### Mostrando Notificações
Agora nós podemos ir em frente e adicionar uma lista de notificações ao cabeçalho:
~~~html
<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>
~~~
<%= caption "client/views/includes/header.html" %>
<%= highlight "12~19" %>
E criar os templates `notifications` e `notification` (eles compartilharão um mesmo arquivo `notifications.html`):
~~~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>
~~~
<%= caption "client/views/notifications/notifications.html" %>
Nós podemos ver que o plano é que cada notificação contenha um link para o artigo que foi comentado, e o nome do usuário que comentou.
Em seguida, nós precisamos ter certeza que nós selecionamos a lista certa de notificações no nosso administrador, e atualizamos as notificações como "lidas" quando o usuário clica no link para onde elas apontam.
~~~js
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}});
}
})
~~~
<%= caption "client/views/notifications/notifications.js" %>
<%= commit "11-2", "Display notifications in the header." %>
Você pode pensar que notificações não são tão diferentes de erros, e é verdade que a estrutura deles é bem similar. Há uma grande diferença porém: nós criamos uma coleção do lado do cliente sincronizada. Isto significa que nossas notificações são *persistentes* e, desde que nós usemos a mesma conta de usuário, elas existirão ao longo de atualizações do navegador e diferentes dispositivos.
Tente: abra um segundo navegador (digamos o Firefox), crie uma nova conta de usuário, e comente num artigo que você criou com sua conta principal (a qual você deixou aberta no Chrome). Você deve ver algo assim:
<%= screenshot "11-1", "Displaying notifications." %>
### Controlando acesso às notificações
As notificações estão funcionando devidamente. Entretanto há um pequeno problema: nossas notificações são públicas.
Se você ainda tiver seu segundo navegador aberto, tente rodar o código seguinte no console do navegador:
~~~js
❯ Notifications.find().count();
1
~~~
<%= caption "Browser console" %>
Este novo usuário (aquele que *comentou*) não deve ter nenhuma notificação. As notificações que eles podem ver na coleção `Notifications` na verdade pertencem ao nosso usuário *original*.
Pondo de lado questões de privacidade em potencial, nós simplesmente não podemos ter todas notificações de cada usuário sendo lidas no navegador dos outros usuários. Num site grande o suficiente, isso poderia sobrecarregar a memória disponível do navegador e começar a causar sérios problemas de performance.
Nós solucionamos essa questão com **publicações**. Nós podemos usar nossas publicações para especificar precisamente que parte da nossa coleção nós queremos compartilhar com cada navegador.
Para conseguir isso, nós precisamos retornar um cursor diferente de `Notifications.find()` na nossa publicação. Nós queremos retornar um cursor que corresponda às notificações do usuário logado.
Fazer isso é bem linear, já que uma função `publish` tem disponível a `_id` do usuário logado em `this.userId`:
~~~js
Meteor.publish('notifications', function() {
return Notifications.find({userId: this.userId});
});
~~~
<%= caption "server/publications.js" %>
<%= commit "11-3", "Only sync notifications that are relevant to the user." %>
Agora se nós checarmos nas nossas duas janelas de navegador, nós devemos ver duas coleções de notificações diferentes:
~~~js
❯ Notifications.find().count();
1
~~~
<%= caption "Browser console (user 1)" %>
~~~js
❯ Notifications.find().count();
0
~~~
<%= caption "Browser console (user 2)" %>
Na verdade, a lista de Notificações deve até mudar quando o usuário logar e deslogar do aplicativo. Isto é porque publicações automaticamente re-publicam toda vez que a conta de usuário muda.
Nosso aplicativo está se tornando cada vez mais e mais funcional, e assim que mais usuários se cadastrarem e começarem a postar links nós correremos o risco de terminar com uma homepage sem fim. Nós checaremos isso no próximo capítulo ao implementar paginação.