From 51395dda91b72deafce61c3a88a194ac680c2dc1 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Tue, 22 Oct 2013 14:22:16 -0400 Subject: [PATCH 1/4] updated all RDB.hget in posts to use getPostField. new post filters for retrieving and saving posts. made editPost saving synchronous. --- src/postTools.js | 17 +++++++++++--- src/posts.js | 57 ++++++++++++++++++++++++++++++++++++---------- src/threadTools.js | 2 +- 3 files changed, 60 insertions(+), 16 deletions(-) diff --git a/src/postTools.js b/src/postTools.js index ca3dd2dae0..bdf69943d6 100644 --- a/src/postTools.js +++ b/src/postTools.js @@ -61,9 +61,20 @@ var RDB = require('./redis.js'), PostTools.edit = function(uid, pid, title, content) { var success = function() { - posts.setPostField(pid, 'content', content); - posts.setPostField(pid, 'edited', Date.now()); - posts.setPostField(pid, 'editor', uid); + async.waterfall([ + function(next) { + posts.setPostField(pid, 'edited', Date.now()); + next(null); + }, + function(next) { + posts.setPostField(pid, 'editor', uid); + next(null); + }, + function(next) { + posts.setPostField(pid, 'content', content); + next(null); + } + ]); postSearch.remove(pid, function() { postSearch.index(content, pid); diff --git a/src/posts.js b/src/posts.js index ef285a8bcd..248e042d1e 100644 --- a/src/posts.js +++ b/src/posts.js @@ -24,9 +24,16 @@ var RDB = require('./redis.js'), RDB.handle(err); if (pids.length) { - Posts.getPostsByPids(pids, function(err, posts) { - callback(posts); - }); + plugins.fireHook('filter:post.getTopic', pids, function(err, posts) { + if (!err & 0 < posts.length) { + Posts.getPostsByPids(pids, function(err, posts) { + plugins.fireHook('action:post.gotTopic', posts); + callback(posts); + }); + } else { + callback(posts); + } + }); } else { callback([]); } @@ -131,6 +138,7 @@ var RDB = require('./redis.js'), }); } + // TODO: this function is never called except from some debug route. clean up? Posts.getPostData = function(pid, callback) { RDB.hgetall('post:' + pid, function(err, data) { if (err === null) { @@ -146,7 +154,14 @@ var RDB = require('./redis.js'), Posts.getPostFields = function(pid, fields, callback) { RDB.hmgetObject('post:' + pid, fields, function(err, data) { if (err === null) { - callback(data); + // TODO: I think the plugins system needs an optional 'parameters' paramter so I don't have to do this: + data = data || {}; + data.pid = pid; + data.fields = fields; + + plugins.fireHook('filter:post.getFields', data, function(err, data) { + callback(data); + }); } else { console.log(err); } @@ -155,15 +170,28 @@ var RDB = require('./redis.js'), Posts.getPostField = function(pid, field, callback) { RDB.hget('post:' + pid, field, function(err, data) { - if (err === null) - callback(data); - else + if (err === null) { + // TODO: I think the plugins system needs an optional 'parameters' paramter so I don't have to do this: + data = data || {}; + data.pid = pid; + data.field = field; + + plugins.fireHook('filter:post.getField', data, function(err, data) { + callback(data); + }); + } else { console.log(err); + } }); } - Posts.setPostField = function(pid, field, value) { + Posts.setPostField = function(pid, field, value, done) { RDB.hset('post:' + pid, field, value); + plugins.fireHook('action:post.setField', { + 'pid': pid, + 'field': field, + 'value': value + }, done); } Posts.getPostsByPids = function(pids, callback) { @@ -402,11 +430,16 @@ var RDB = require('./redis.js'), Posts.getPostsByUid = function(uid, start, end, callback) { user.getPostIds(uid, start, end, function(pids) { - if (pids && pids.length) { - - Posts.getPostsByPids(pids, function(err, posts) { - callback(posts); + plugins.fireHook('filter:post.getTopic', pids, function(err, posts) { + if (!err & 0 < posts.length) { + Posts.getPostsByPids(pids, function(err, posts) { + plugins.fireHook('action:post.gotTopic', posts); + callback(posts); + }); + } else { + callback(posts); + } }); } else callback([]); diff --git a/src/threadTools.js b/src/threadTools.js index 478f25fd7e..e375876ef0 100644 --- a/src/threadTools.js +++ b/src/threadTools.js @@ -303,7 +303,7 @@ var RDB = require('./redis.js'), pids.reverse(); async.detectSeries(pids, function(pid, next) { - RDB.hget('post:' + pid, 'deleted', function(err, deleted) { + posts.getPostField(pid, 'deleted', function(deleted) { if (deleted === '0') next(true); else next(false); }); From 2ee29683a7de070f24c813ea2e986e46ead54d1d Mon Sep 17 00:00:00 2001 From: psychobunny Date: Tue, 22 Oct 2013 14:22:53 -0400 Subject: [PATCH 2/4] random whitespace --- src/posts.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/posts.js b/src/posts.js index 248e042d1e..5199be6f2d 100644 --- a/src/posts.js +++ b/src/posts.js @@ -428,7 +428,6 @@ var RDB = require('./redis.js'), } Posts.getPostsByUid = function(uid, start, end, callback) { - user.getPostIds(uid, start, end, function(pids) { if (pids && pids.length) { plugins.fireHook('filter:post.getTopic', pids, function(err, posts) { From 705571de8ca3db8a8976eb92ec30e1812c4e3b50 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 22 Oct 2013 14:35:20 -0400 Subject: [PATCH 3/4] notifications page + ajaxify route + css styling --- public/language/en/notifications.json | 3 +++ public/src/forum/notifications.js | 16 ++++++++++++++ public/templates/notifications.tpl | 14 +++++++++++++ src/routes/api.js | 10 +++++++++ src/user.js | 30 +++++++++++++++++++++++++++ src/webserver.js | 2 +- 6 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 public/language/en/notifications.json create mode 100644 public/src/forum/notifications.js create mode 100644 public/templates/notifications.tpl diff --git a/public/language/en/notifications.json b/public/language/en/notifications.json new file mode 100644 index 0000000000..212cf3fa4f --- /dev/null +++ b/public/language/en/notifications.json @@ -0,0 +1,3 @@ +{ + "title": "Notifications" +} \ No newline at end of file diff --git a/public/src/forum/notifications.js b/public/src/forum/notifications.js new file mode 100644 index 0000000000..38bb3c80a2 --- /dev/null +++ b/public/src/forum/notifications.js @@ -0,0 +1,16 @@ +define(function() { + var Notifications = {}; + + Notifications.init = function() { + var listEl = $('.notifications-list'); + + $('span.timeago').timeago(); + + // Allow the user to click anywhere in the LI + listEl.on('click', 'li', function(e) { + this.querySelector('a').click(); + }); + } + + return Notifications; +}); \ No newline at end of file diff --git a/public/templates/notifications.tpl b/public/templates/notifications.tpl new file mode 100644 index 0000000000..d509293009 --- /dev/null +++ b/public/templates/notifications.tpl @@ -0,0 +1,14 @@ +

[[notifications:title]]

+ + + + \ No newline at end of file diff --git a/src/routes/api.js b/src/routes/api.js index 78823bd9bd..b3c9afde81 100644 --- a/src/routes/api.js +++ b/src/routes/api.js @@ -154,6 +154,16 @@ var user = require('./../user.js'), }); }); + app.get('/notifications', function(req, res) { + if (req.user && req.user.uid) { + user.notifications.getAll(req.user.uid, null, null, function(err, notifications) { + res.json({ + notifications: notifications + }); + }); + } else res.send(403); + }); + app.get('/confirm/:id', function (req, res) { user.email.confirm(req.params.id, function (data) { if (data.status === 'ok') { diff --git a/src/user.js b/src/user.js index 4b267457e1..9a0092a21f 100644 --- a/src/user.js +++ b/src/user.js @@ -945,6 +945,36 @@ var utils = require('./../public/src/utils.js'), callback(notifications); }); }, + getAll: function(uid, limit, before, callback) { + var now = new Date(); + + if (!limit || parseInt(limit) <= 0) limit = 25; + if (before) before = new Date(parseInt(before, 10)); + + RDB.multi() + .zrevrangebyscore('uid:' + uid + ':notifications:read', before ? before.getTime(): now.getTime(), -Infinity, 'LIMIT', 0, limit) + .zrevrangebyscore('uid:' + uid + ':notifications:unread', before ? before.getTime(): now.getTime(), -Infinity, 'LIMIT', 0, limit) + .exec(function(err, results) { + // Merge the read and unread notifications + var nids = results[0].concat(results[1]); + + async.map(nids, function(nid, next) { + notifications.get(nid, function(notif_data) { + next(null, notif_data); + }); + }, function(err, notifs) { + notifs = notifs.sort(function(a, b) { + return parseInt(b.datetime, 10) - parseInt(a.datetime, 10); + }).map(function(notif) { + notif.datetimeISO = new Date(parseInt(notif.datetime, 10)).toISOString(); + + return notif; + }); + + callback(err, notifs); + }) + }); + }, getUnreadCount: function(uid, callback) { RDB.zcount('uid:' + uid + ':notifications:unread', 0, 10, callback); }, diff --git a/src/webserver.js b/src/webserver.js index 69e8f6803b..78485cb55d 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -282,7 +282,7 @@ var express = require('express'), // Basic Routes (entirely client-side parsed, goal is to move the rest of the crap in this file into this one section) (function () { - var routes = ['login', 'register', 'account', 'recent', 'unread', 'popular', 'active', '403', '404']; + var routes = ['login', 'register', 'account', 'recent', 'unread', 'notifications', '403', '404']; for (var i = 0, ii = routes.length; i < ii; i++) { (function (route) { From 62c85274a32fe31a92fa008c05f08d6cbe354a6c Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 22 Oct 2013 15:06:07 -0400 Subject: [PATCH 4/4] mark all notifs as read functionality. CSS tweaks... --- public/src/forum/notifications.js | 17 ++++++++++++++- public/templates/notifications.tpl | 4 ++-- src/notifications.js | 35 ++++++++++++++++++------------ src/upgrade.js | 2 +- src/user.js | 9 ++++---- 5 files changed, 45 insertions(+), 22 deletions(-) diff --git a/public/src/forum/notifications.js b/public/src/forum/notifications.js index 38bb3c80a2..de1e053871 100644 --- a/public/src/forum/notifications.js +++ b/public/src/forum/notifications.js @@ -2,7 +2,8 @@ define(function() { var Notifications = {}; Notifications.init = function() { - var listEl = $('.notifications-list'); + var listEl = $('.notifications-list'), + markAllReadEl = document.getElementById('mark-all-notifs-read'); $('span.timeago').timeago(); @@ -10,6 +11,20 @@ define(function() { listEl.on('click', 'li', function(e) { this.querySelector('a').click(); }); + + // Mark all as read button + $(markAllReadEl).click(function() { + socket.emit('api:notifications.mark_all_read', {}, function() { + ajaxify.go('notifications'); + app.alert({ + alert_id: "notifications:mark_all_read", + title: "All Notifications Read", + message: "Successfully marked all notifications read", + type: 'success', + timeout: 2500 + }) + }); + }); } return Notifications; diff --git a/public/templates/notifications.tpl b/public/templates/notifications.tpl index d509293009..beac732ef1 100644 --- a/public/templates/notifications.tpl +++ b/public/templates/notifications.tpl @@ -1,10 +1,10 @@

[[notifications:title]]

- +
    -
  • +
  • diff --git a/src/notifications.js b/src/notifications.js index d2e1477aac..d6b2791028 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -3,17 +3,24 @@ var RDB = require('./redis.js'), utils = require('../public/src/utils.js'), notifications = { - get: function(nid, callback) { - RDB.hmget('notifications:' + nid, 'text', 'score', 'path', 'datetime', 'uniqueId', function(err, notification) { - callback({ - nid: nid, - text: notification[0], - score: notification[1], - path: notification[2], - datetime: notification[3], - uniqueId: notification[4] + get: function(nid, uid, callback) { + RDB.multi() + .hmget('notifications:' + nid, 'text', 'score', 'path', 'datetime', 'uniqueId') + .zrank('uid:' + uid + ':notifications:read', nid) + .exec(function(err, results) { + var notification = results[0] + readIdx = results[1]; + + callback({ + nid: nid, + text: notification[0], + score: notification[1], + path: notification[2], + datetime: notification[3], + uniqueId: notification[4], + read: readIdx !== null ? true : false + }); }); - }); }, create: function(text, path, uniqueId, callback) { /** @@ -39,7 +46,7 @@ var RDB = require('./redis.js'), var numUids = uids.length, x; - notifications.get(nid, function(notif_data) { + notifications.get(nid, null, function(notif_data) { for (x = 0; x < numUids; x++) { if (parseInt(uids[x]) > 0) { (function(uid) { @@ -59,7 +66,7 @@ var RDB = require('./redis.js'), RDB.zrange('uid:' + uid + ':notifications:unread', 0, -1, function(err, nids) { if (nids && nids.length > 0) { async.each(nids, function(nid, next) { - notifications.get(nid, function(nid_info) { + notifications.get(nid, uid, function(nid_info) { if (nid_info.uniqueId === uniqueId) RDB.zrem('uid:' + uid + ':notifications:unread', nid); next(); }); @@ -73,7 +80,7 @@ var RDB = require('./redis.js'), RDB.zrange('uid:' + uid + ':notifications:read', 0, -1, function(err, nids) { if (nids && nids.length > 0) { async.each(nids, function(nid, next) { - notifications.get(nid, function(nid_info) { + notifications.get(nid, uid, function(nid_info) { if (nid_info.uniqueId === uniqueId) RDB.zrem('uid:' + uid + ':notifications:read', nid); next(); }); @@ -89,7 +96,7 @@ var RDB = require('./redis.js'), }, mark_read: function(nid, uid, callback) { if (parseInt(uid) > 0) { - notifications.get(nid, function(notif_data) { + notifications.get(nid, uid, function(notif_data) { RDB.zrem('uid:' + uid + ':notifications:unread', nid); RDB.zadd('uid:' + uid + ':notifications:read', notif_data.datetime, nid); if (callback) callback(); diff --git a/src/upgrade.js b/src/upgrade.js index 153c7c0399..9a6faed535 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -31,7 +31,7 @@ Upgrade.upgrade = function() { async.each(keys, function(key, next) { RDB.zrange(key, 0, -1, function(err, nids) { async.each(nids, function(nid, next) { - notifications.get(nid, function(notif_data) { + notifications.get(nid, null, function(notif_data) { RDB.zadd(key, notif_data.datetime, nid, next); }); }, next); diff --git a/src/user.js b/src/user.js index 9a0092a21f..47e86e69f1 100644 --- a/src/user.js +++ b/src/user.js @@ -902,7 +902,7 @@ var utils = require('./../public/src/utils.js'), if (nids && nids.length > 0) { async.eachSeries(nids, function(nid, next) { - notifications.get(nid, function(notif_data) { + notifications.get(nid, uid, function(notif_data) { unread.push(notif_data); next(); }); @@ -924,7 +924,7 @@ var utils = require('./../public/src/utils.js'), if (nids && nids.length > 0) { async.eachSeries(nids, function(nid, next) { - notifications.get(nid, function(notif_data) { + notifications.get(nid, uid, function(notif_data) { read.push(notif_data); next(); }); @@ -959,7 +959,7 @@ var utils = require('./../public/src/utils.js'), var nids = results[0].concat(results[1]); async.map(nids, function(nid, next) { - notifications.get(nid, function(notif_data) { + notifications.get(nid, uid, function(notif_data) { next(null, notif_data); }); }, function(err, notifs) { @@ -967,6 +967,7 @@ var utils = require('./../public/src/utils.js'), return parseInt(b.datetime, 10) - parseInt(a.datetime, 10); }).map(function(notif) { notif.datetimeISO = new Date(parseInt(notif.datetime, 10)).toISOString(); + notif.readClass = !notif.read ? 'unread' : ''; return notif; }); @@ -981,7 +982,7 @@ var utils = require('./../public/src/utils.js'), getUnreadByUniqueId: function(uid, uniqueId, callback) { RDB.zrange('uid:' + uid + ':notifications:unread', 0, -1, function(err, nids) { async.filter(nids, function(nid, next) { - notifications.get(nid, function(notifObj) { + notifications.get(nid, uid, function(notifObj) { if (notifObj.uniqueId === uniqueId) next(true); else next(false); });