mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-12-22 16:30:34 +01:00
added a new section that only shows unread topics, added mark all read button, closes #140
This commit is contained in:
@@ -751,6 +751,10 @@ body .navbar .nodebb-inline-block {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#mark-allread-btn {
|
||||||
|
margin-bottom:15px;
|
||||||
|
}
|
||||||
|
|
||||||
@-webkit-keyframes scroll-2 /* Safari and Chrome */
|
@-webkit-keyframes scroll-2 /* Safari and Chrome */
|
||||||
{
|
{
|
||||||
0% {top: 0px;}
|
0% {top: 0px;}
|
||||||
|
|||||||
@@ -44,7 +44,13 @@
|
|||||||
socket.on('event:new_post', function(data) {
|
socket.on('event:new_post', function(data) {
|
||||||
++newPostCount;
|
++newPostCount;
|
||||||
updateAlertText();
|
updateAlertText();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#mark-allread-btn').on('click', function() {
|
||||||
|
socket.emit('api:topics.markAllRead');
|
||||||
|
$(this).remove();
|
||||||
|
$('#topics-container').empty();
|
||||||
|
$('#category-no-topics').removeClass('hidden');
|
||||||
});
|
});
|
||||||
|
|
||||||
})();
|
})();
|
||||||
@@ -27,6 +27,7 @@
|
|||||||
"users/[^]*": "account",
|
"users/[^]*": "account",
|
||||||
|
|
||||||
"recent": "recent",
|
"recent": "recent",
|
||||||
|
"unread": "unread",
|
||||||
"popular": "category",
|
"popular": "category",
|
||||||
"active": "category"
|
"active": "category"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -49,10 +49,10 @@
|
|||||||
<li>
|
<li>
|
||||||
<a href="/recent">Recent <!--<span class="badge badge-inverse">3</span>--></a>
|
<a href="/recent">Recent <!--<span class="badge badge-inverse">3</span>--></a>
|
||||||
</li>
|
</li>
|
||||||
<!--<li>
|
|
||||||
<a href="/popular">Popular</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
<li>
|
||||||
|
<a href="/unread">Unread</a>
|
||||||
|
</li>
|
||||||
|
<!--<li>
|
||||||
<a href="/active">Active</a>
|
<a href="/active">Active</a>
|
||||||
</li>-->
|
</li>-->
|
||||||
<li>
|
<li>
|
||||||
|
|||||||
@@ -15,8 +15,6 @@
|
|||||||
<strong>There are no recent topics.</strong>
|
<strong>There are no recent topics.</strong>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="btn">Mark All Read</button><br/><br/>
|
|
||||||
|
|
||||||
<div class="category row">
|
<div class="category row">
|
||||||
<div class="{topic_row_size}">
|
<div class="{topic_row_size}">
|
||||||
<ul id="topics-container">
|
<ul id="topics-container">
|
||||||
|
|||||||
54
public/templates/unread.tpl
Normal file
54
public/templates/unread.tpl
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<div class="container">
|
||||||
|
<ul class="breadcrumb">
|
||||||
|
<li><a href="/">Home</a><span class="divider">/</span></li>
|
||||||
|
<li class="active">{category_name}</li>
|
||||||
|
<div id="category_active_users"></div>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<a href="/unread">
|
||||||
|
<div class="alert hide" id="new-topics-alert"></div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="alert alert-warning {no_topics_message}" id="category-no-topics">
|
||||||
|
<strong>There are no unread topics.</strong>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<button id="mark-allread-btn" class="btn {show_markallread_button}">Mark All Read</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="category row">
|
||||||
|
<div class="{topic_row_size}">
|
||||||
|
<ul id="topics-container">
|
||||||
|
<!-- BEGIN topics -->
|
||||||
|
<a href="../../topic/{topics.slug}" id="tid-{topics.tid}">
|
||||||
|
<li class="category-item {topics.deleted-class}">
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="span12 topic-row img-polaroid">
|
||||||
|
<div class="latest-post visible-desktop">
|
||||||
|
<div class="pull-right">
|
||||||
|
<img style="width: 48px; height: 48px; /*temporary*/" src="{topics.teaser_userpicture}" />
|
||||||
|
<p><strong>{topics.teaser_username}</strong>: {topics.teaser_text}</p>
|
||||||
|
<span>posted {topics.teaser_timestamp} ago</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3><span class="topic-title"><span class="badge {topics.badgeclass}">{topics.postcount}</span>{topics.title}</span></h3>
|
||||||
|
<small>
|
||||||
|
<strong><i class="{topics.pin-icon}"></i><i class="{topics.lock-icon}"></i></strong>
|
||||||
|
Posted {topics.relativeTime} ago by
|
||||||
|
<strong>{topics.username}</strong>.
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</a>
|
||||||
|
<!-- END topics -->
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="{relative_path}/src/forum/recent.js"></script>
|
||||||
@@ -307,7 +307,7 @@ var RDB = require('./redis.js'),
|
|||||||
|
|
||||||
feed.updateTopic(tid, cid);
|
feed.updateTopic(tid, cid);
|
||||||
|
|
||||||
RDB.zadd('categories:recent_posts:cid:' + cid, Date.now(), pid);
|
RDB.zadd('categories:recent_posts:cid:' + cid, timestamp, pid);
|
||||||
|
|
||||||
// this is a bit of a naive implementation, defn something to look at post-MVP
|
// this is a bit of a naive implementation, defn something to look at post-MVP
|
||||||
RDB.scard('cid:' + cid + ':active_users', function(amount) {
|
RDB.scard('cid:' + cid + ':active_users', function(amount) {
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
return {
|
return {
|
||||||
|
|
||||||
/* sets */
|
/* sets */
|
||||||
tid: 'topics:tid',
|
|
||||||
read_by_uid: 'tid:' + tid + ':read_by_uid',
|
read_by_uid: 'tid:' + tid + ':read_by_uid',
|
||||||
|
|
||||||
/* sorted sets */
|
/* sorted sets */
|
||||||
|
|||||||
@@ -94,8 +94,6 @@ marked.setOptions({
|
|||||||
|
|
||||||
var timestamp = Date.now();
|
var timestamp = Date.now();
|
||||||
|
|
||||||
RDB.zremrangebyscore('topics:recent', '-inf', timestamp - 86400000);
|
|
||||||
|
|
||||||
RDB.zrevrangebyscore([ 'topics:recent', '+inf', timestamp - 86400000], function(err, tids) {
|
RDB.zrevrangebyscore([ 'topics:recent', '+inf', timestamp - 86400000], function(err, tids) {
|
||||||
|
|
||||||
var latestTopics = {
|
var latestTopics = {
|
||||||
@@ -121,10 +119,59 @@ marked.setOptions({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Topics.getUnreadTopics = function(uid, start, stop, callback) {
|
||||||
|
|
||||||
|
var unreadTopics = {
|
||||||
|
'category_name' : 'Unread',
|
||||||
|
'show_sidebar' : 'hidden',
|
||||||
|
'show_topic_button' : 'hidden',
|
||||||
|
'show_markallread_button': 'show',
|
||||||
|
'no_topics_message' : 'hidden',
|
||||||
|
'topic_row_size': 'span12',
|
||||||
|
'topics' : []
|
||||||
|
};
|
||||||
|
|
||||||
|
RDB.zrevrange('topics:recent', start, stop, function (err, tids) {
|
||||||
|
|
||||||
|
function noUnreadTopics() {
|
||||||
|
unreadTopics.no_topics_message = 'show';
|
||||||
|
unreadTopics.show_markallread_button = 'hidden';
|
||||||
|
callback(unreadTopics);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tids || !tids.length) {
|
||||||
|
noUnreadTopics();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Topics.hasReadTopics(tids, uid, function(read) {
|
||||||
|
|
||||||
|
var unreadTids = tids.filter(function(tid, index, self) {
|
||||||
|
return read[index] === 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!unreadTids || !unreadTids.length) {
|
||||||
|
noUnreadTopics();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Topics.getTopicsByTids(unreadTids, uid, function(topicData) {
|
||||||
|
unreadTopics.topics = topicData;
|
||||||
|
callback(unreadTopics);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Topics.getTopicsByTids = function(tids, current_user, callback, category_id) {
|
Topics.getTopicsByTids = function(tids, current_user, callback, category_id) {
|
||||||
|
|
||||||
var retrieved_topics = [];
|
var retrieved_topics = [];
|
||||||
|
|
||||||
|
if(!Array.isArray(tids) || tids.length === 0) {
|
||||||
|
callback(retrieved_topics);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
function getTopicInfo(topicData, callback) {
|
function getTopicInfo(topicData, callback) {
|
||||||
|
|
||||||
function getUserName(next) {
|
function getUserName(next) {
|
||||||
@@ -134,20 +181,20 @@ marked.setOptions({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function hasReadTopic(next) {
|
function hasReadTopic(next) {
|
||||||
topics.hasReadTopic(topicData.tid, current_user, function(hasRead) {
|
Topics.hasReadTopic(topicData.tid, current_user, function(hasRead) {
|
||||||
next(null, hasRead);
|
next(null, hasRead);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTeaserInfo(next) {
|
function getTeaserInfo(next) {
|
||||||
topics.getTeaser(topicData.tid, function(err, teaser) {
|
Topics.getTeaser(topicData.tid, function(err, teaser) {
|
||||||
next(null, teaser || {});
|
next(null, teaser || {});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// temporary. I don't think this call should belong here
|
// temporary. I don't think this call should belong here
|
||||||
function getPrivileges(next) {
|
function getPrivileges(next) {
|
||||||
Categories.privileges(category_id, current_user, function(user_privs) {
|
categories.privileges(category_id, current_user, function(user_privs) {
|
||||||
next(null, user_privs);
|
next(null, user_privs);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -173,7 +220,7 @@ marked.setOptions({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function loadTopic(tid, callback) {
|
function loadTopic(tid, callback) {
|
||||||
topics.getTopicData(tid, function(topicData) {
|
Topics.getTopicData(tid, function(topicData) {
|
||||||
if(!topicData) {
|
if(!topicData) {
|
||||||
return callback(null);
|
return callback(null);
|
||||||
}
|
}
|
||||||
@@ -354,10 +401,11 @@ marked.setOptions({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Topics.markAllRead = function(uid) {
|
Topics.markAllRead = function(uid, callback) {
|
||||||
RDB.smembers('topics:tid', function(err, tids) {
|
RDB.smembers('topics:tid', function(err, tids) {
|
||||||
if(err) {
|
if(err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
callback(err, null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -366,6 +414,8 @@ marked.setOptions({
|
|||||||
Topics.markAsRead(tids[i], uid);
|
Topics.markAsRead(tids[i], uid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callback(null, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -513,7 +563,7 @@ marked.setOptions({
|
|||||||
// Global Topics
|
// Global Topics
|
||||||
if (uid == null) uid = 0;
|
if (uid == null) uid = 0;
|
||||||
if (uid !== null) {
|
if (uid !== null) {
|
||||||
RDB.sadd(schema.topics().tid, tid);
|
RDB.sadd('topics:tid', tid);
|
||||||
} else {
|
} else {
|
||||||
// need to add some unique key sent by client so we can update this with the real uid later
|
// need to add some unique key sent by client so we can update this with the real uid later
|
||||||
RDB.lpush(schema.topics().queued_tids, tid);
|
RDB.lpush(schema.topics().queued_tids, tid);
|
||||||
|
|||||||
@@ -150,7 +150,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)
|
// Basic Routes (entirely client-side parsed, goal is to move the rest of the crap in this file into this one section)
|
||||||
(function() {
|
(function() {
|
||||||
var routes = ['login', 'register', 'account', 'recent', 'popular', 'active', '403', '404'];
|
var routes = ['login', 'register', 'account', 'recent', 'unread', 'popular', 'active', '403', '404'];
|
||||||
|
|
||||||
for (var i=0, ii=routes.length; i<ii; i++) {
|
for (var i=0, ii=routes.length; i<ii; i++) {
|
||||||
(function(route) {
|
(function(route) {
|
||||||
@@ -388,6 +388,16 @@ var express = require('express'),
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.get('/test', function(req, res) {
|
||||||
|
topics.getUnreadTopics(4, 0, 9, function(data) {
|
||||||
|
console.log('ll', data.topics.length);
|
||||||
|
topics.getUnreadTopics(4, data.topics.length, data.topics.length + 9, function(data2) {
|
||||||
|
var finalData = [data,data2];
|
||||||
|
res.json(finalData);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// These functions are called via ajax once the initial page is loaded to populate templates with data
|
// These functions are called via ajax once the initial page is loaded to populate templates with data
|
||||||
@@ -482,19 +492,24 @@ var express = require('express'),
|
|||||||
}, req.params.id, uid);
|
}, req.params.id, uid);
|
||||||
break;
|
break;
|
||||||
case 'recent' :
|
case 'recent' :
|
||||||
topics.getLatestTopics(uid, 0, 9, function(data) {
|
topics.getLatestTopics(uid, 0, 9, function(data) {
|
||||||
res.json(data);
|
res.json(data);
|
||||||
});
|
});
|
||||||
|
break;
|
||||||
|
case 'unread':
|
||||||
|
topics.getUnreadTopics(uid, 0, -1, function(data) {
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
case 'popular' :
|
case 'popular' :
|
||||||
topics.getLatestTopics(uid, 0, 9, function(data) {
|
topics.getLatestTopics(uid, 0, 9, function(data) {
|
||||||
res.json(data);
|
res.json(data);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'active' :
|
case 'active' :
|
||||||
topics.getLatestTopics(uid, 0, 9, function(data) {
|
topics.getLatestTopics(uid, 0, 9, function(data) {
|
||||||
res.json(data);
|
res.json(data);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'confirm':
|
case 'confirm':
|
||||||
user.email.confirm(req.params.id, function(data) {
|
user.email.confirm(req.params.id, function(data) {
|
||||||
|
|||||||
@@ -338,6 +338,19 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
|
|||||||
topics.post(socket, uid, data.title, data.content, data.category_id, data.images);
|
topics.post(socket, uid, data.title, data.content, data.category_id, data.images);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on('api:topics.markAllRead', function(data) {
|
||||||
|
topics.markAllRead(uid, function(err, success) {
|
||||||
|
if(!err && success) {
|
||||||
|
socket.emit('event:alert', {
|
||||||
|
title: 'Success',
|
||||||
|
message: 'All topics marked as read!',
|
||||||
|
type: 'success',
|
||||||
|
timeout: 2000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
socket.on('api:posts.reply', function(data) {
|
socket.on('api:posts.reply', function(data) {
|
||||||
posts.reply(socket, data.topic_id, uid, data.content, data.images);
|
posts.reply(socket, data.topic_id, uid, data.content, data.images);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user