-
Notifications
You must be signed in to change notification settings - Fork 11
/
11-notifications.md.erb
326 lines (267 loc) · 12.5 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
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
---
title: 通知機能
slug: notifications
date: 0011/01/01
number: 11
points: 10
photoUrl: http://www.flickr.com/photos/ikewinski/8719868039/
photoAuthor: Mike Lewinski
contents: 他のユーザのアクションをユーザーに通知するための通知コレクションを追加します|ユーザと関連する通知のみを共有する方法を学びます|Mateorパブリケーションとサブスクリプションの詳細についてを学びます
version: 1.7.1
---
これでユーザーはお互いの投稿にコメントできるようになったので、
会話が始まったことを知らせると良いでしょう。
そのようにするために、投稿にコメントが付いたことを投稿者に知らせて、
そのコメントを見るためのリンクを提供します。
これは Meteor の機能がよく輝くところです:
Meteor はデフォルトでリアルタイムなので、そうした通知は_瞬時_に表示されます。
ユーザーがページを再読み込みしたり、チェックしたりすることを私たちは待つ必要はありません。
特別なコードを書くことなく、新しい通知をポップアップできるからです。
### 通知を作成
投稿に誰かがコメントをした時の通知を作っていきます。
将来的には、通知を多くのシナリオをカバーするように拡張できますが、
今のところはユーザーに何が起きているか知らせることで十分でしょう。
では、`Notifications`コレクションを作っていきましょう。
自分の投稿の新しいコメントごとにマッチする通知を挿入する
`createCommentNotification`関数を作っていきます。
クライアントからの通知を更新されますので、
`allow`呼び出しが防弾(?bulletproof)であることを確認する必要があります。:
- `update`呼び出しを行うユーザーは、変更されている通知を所有している。
- ユーザーは、単一のフィールドを更新しようとしている。
- その単一のフィールドは、私たちの通知の`read`プロパティです。
~~~js
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
});
}
};
~~~
<%= caption "lib/collections/notifications.js" %>
投稿やコメントと同じように、`Notifications`コレクションはクライアントとサーバーの両方で共有されます。
一度ユーザーが通知を見た時に、私たちは通知を更新する必要があるので、アップデートできるようにします。 いつものように、更新許可をユーザー自身のデータにリダイレクトするようにします。
私たちはシンプルな関数を作りました。これはユーザーがコメントしている投稿に目を向け、
誰がそこから通知されるか見つけて、新しい通知を挿入する関数です。
私たちはすでに サーバーサイドのメソッドでコメントを作っているので、
関数を呼び出すために、このメソッドを増やすことができます。
新しく作ったコメントの`_id`を変数に保存するため、
`return Comments.insert(comment);`を`comment._id = Comments.insert(comment)`に
取り替えます。それから、`createCommentNotification`関数を呼び出します。:
~~~js
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;
}
});
~~~
<%= caption "lib/collections/comments.js" %>
<%= highlight "17~123" %>
通知もパブリッシュしましょう:
~~~js
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();
});
~~~
<%= caption "server/publications.js" %>
<%= highlight "10~12" %>
そしてクライアントでサブスクライブします。:
~~~js
Router.configure({
layoutTemplate: 'layout',
loadingTemplate: 'loading',
notFoundTemplate: 'notFound',
waitOn: function() {
return [Meteor.subscribe('posts'), Meteor.subscribe('notifications')]
}
});
~~~
<%= caption "lib/router.js" %>
<%= highlight "6" %>
<%= commit "11-1", "Added basic notifications collection." %>
### Displaying Notifications
これで私たちは前に進むことができるようになり、ヘッダーに通知のリストを追加できます。
~~~html
<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>
~~~
<%= caption "client/templates/includes/header.html" %>
<%= highlight "15~22" %>
そして、notifications テンプレートと notification テンプレートをつくります。
(この2つは1つの 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}}
{{> 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>
~~~
<%= caption "client/templates/notifications/notifications.html" %>
私たちはコメントされた投稿へのリンクがあるそれぞれの通知のための画面
とコメントされた投稿のユーザーの名前を見ることができます。
それぞれの通知がコメントした投稿へのリンク、
およびその上でコメントしたユーザーの名前を含むようにするための画面があることがわかります。
次に、私たちはマネージャーで、通知の正しいリストを選んで、
ユーザーがポイントしているリンクをクリックした時に、通知を"read"としてアップデートする必要があります。
~~~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.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}});
}
});
~~~
<%= caption "client/templates/notifications/notifications.js" %>
<%= commit "11-2", "Display notifications in the header." %>
通知はエラーと違って、それほど難しくないと思うかもしれません。
確かにこの2つはとても似ている構造をしています。
しかし、1つだけキーとなる違いがあります:
私たちはクライアント-サーバー間を同期した適切なコレクションを作りました。
これは通知が永続的であるという意味であり、
私たちが同じユーザーアカウントを使う限り、
再読み込みするブラウザでも違うデバイスでもこの通知は健在です。
試しにやってみます:
2つめのブラウザーを開きます。(Firefoxだとしましょう)
新しいユーザーアカウントを作って、あなたのメインアカウントで作った投稿にコメントします。
(Chromeを開いたままで)すると、このようになっているはずです。
<%= screenshot "11-1", "Displaying notifications." %>
### 通知へのアクセスをコントロールします。
通知はうまく動いています。しかし、少し問題があります:この通知は公開されているのです。
2つめのブラウザーを開いたままだとしたら、ブラウザーコンソールで次のようなコードを動かしてみましょう。
~~~js
❯ Notifications.find().count();
1
~~~
<%= caption "Browser console" %>
(*コメント*した)この新しいユーザーはすべての通知を持つべきではありません。
ユーザーが`Notifications`コレクションで見ることができる通知は
*original*なユーザーに属しているはずです。
潜在的なプライベートの問題はさておき、
私たちはすべてのユーザーがブラウザで読み込まれるすべてのユーザーの通知を保有する余裕はありません。
十分に大きなサイトでは、
これはブラウザが使用できるメモリをオーバーロードして、深刻なパフォーマンスの問題をもたらすことになります。
この問題は**パブリケーション**で解決します。
各ブラウザーで共有させたいコレクションの部分を正確に規定するために、私たちはパブリケーションを使います。
これをするために、私たちは`Notifications.find()`よりも
パブリケーションで違ったカーソルを返す必要があります。
つまり、私たちは現在のユーザーの通知に対応するカーソルを返したいのです。
そうすることは、十分に簡単です。
`publish`関数は`this.userId`で使うことができる現在のユーザーの`_id`を持つためです。
~~~js
Meteor.publish('notifications', function() {
return Notifications.find({userId: this.userId, read: false});
});
~~~
<%= caption "server/publications.js" %>
<%= commit "11-3", "Only sync notifications that are relevant to the user." %>
私たちが 2つのブラウザーウィンドウをチェックすると、
2つの異なるnotificationsコレクションを見ることができます。:
~~~js
❯ Notifications.find().count();
1
~~~
<%= caption "Browser console (user 1)" %>
~~~js
❯ Notifications.find().count();
0
~~~
<%= caption "Browser console (user 2)" %>
実際に、アプリにログインしたりログアウトした時に、通知のリストが変化するべきです。
それはユーザーアカウントが変化するたびに、 パブリケーションが自動的に再パブリッシュするためです。
我々のアプリは、ますます機能的になりつつあり、多くのユーザーが参加し、
リンクの投稿を開始として、我々は決して終わることのないホームページで終わる危険性があります。
私たちは、ページネーションを実装することによって、次の章でこれに対処します。