Backend for marking feeds/folders as read at a specific timestamp.

This commit is contained in:
Samuel Clay 2013-09-13 15:02:10 -07:00
parent b250369312
commit d8ed12f15b
7 changed files with 88 additions and 63 deletions

View file

@ -404,31 +404,37 @@ class UserSubscription(models.Model):
r.srem(read_stories_key, *stale_story_hashes)
r.srem("RS:%s" % self.feed_id, *stale_story_hashes)
def mark_feed_read(self):
def mark_feed_read(self, cutoff_date=None):
if (self.unread_count_negative == 0
and self.unread_count_neutral == 0
and self.unread_count_positive == 0
and not self.needs_unread_recalc):
return
now = datetime.datetime.utcnow()
recount = True
# Use the latest story to get last read time.
latest_story = MStory.objects(story_feed_id=self.feed.pk).order_by('-story_date').only('story_date').limit(1)
if latest_story and len(latest_story) >= 1:
latest_story_date = latest_story[0]['story_date']\
+ datetime.timedelta(seconds=1)
if cutoff_date:
cutoff_date = cutoff_date + datetime.timedelta(seconds=1)
else:
latest_story_date = now
latest_story = MStory.objects(story_feed_id=self.feed.pk).order_by('-story_date').only('story_date').limit(1)
if latest_story and len(latest_story) >= 1:
cutoff_date = (latest_story[0]['story_date']
+ datetime.timedelta(seconds=1))
else:
cutoff_date = datetime.datetime.utcnow()
recount = False
self.last_read_date = latest_story_date
self.mark_read_date = latest_story_date
self.unread_count_negative = 0
self.unread_count_positive = 0
self.unread_count_neutral = 0
self.unread_count_updated = now
self.oldest_unread_story_date = now
self.needs_unread_recalc = False
self.last_read_date = cutoff_date
self.mark_read_date = cutoff_date
self.oldest_unread_story_date = cutoff_date
if not recount:
self.unread_count_negative = 0
self.unread_count_positive = 0
self.unread_count_neutral = 0
self.unread_count_updated = cutoff_date
self.needs_unread_recalc = False
else:
self.needs_unread_recalc = True
self.save()

View file

@ -1271,9 +1271,11 @@ def mark_story_as_unread(request):
def mark_feed_as_read(request):
r = redis.Redis(connection_pool=settings.REDIS_PUBSUB_POOL)
feed_ids = request.REQUEST.getlist('feed_id')
cutoff_timestamp = int(request.REQUEST.get('cutoff_timestamp', 0))
multiple = len(feed_ids) > 1
code = 1
errors = []
cutoff_date = datetime.datetime.fromtimestamp(cutoff_timestamp) if cutoff_timestamp else None
for feed_id in feed_ids:
if 'social:' in feed_id:
@ -1300,7 +1302,7 @@ def mark_feed_as_read(request):
continue
try:
marked_read = sub.mark_feed_read()
marked_read = sub.mark_feed_read(cutoff_date=cutoff_date)
if marked_read:
r.publish(request.user.username, 'feed:%s' % feed_id)
except IntegrityError, e:

View file

@ -245,18 +245,21 @@ NEWSBLUR.AssetModel = Backbone.Router.extend({
}, callback);
},
mark_feed_as_read: function(feed_id, callback) {
mark_feed_as_read: function(feed_id, cutoff_timestamp, mark_active, callback) {
var self = this;
var feed_ids = _.isArray(feed_id)
? _.select(feed_id, function(f) { return f; })
: [feed_id];
this.make_request('/reader/mark_feed_as_read', {
feed_id: feed_ids
feed_id: feed_ids,
cutoff_timestamp: cutoff_timestamp
}, callback);
if (feed_id == NEWSBLUR.reader.active_feed) {
if (mark_active) {
this.stories.each(function(story) {
if (cutoff_timestamp &&
parseInt(story.get('story_timestamp'), 10) > cutoff_timestamp) return;
story.set('read_status', true);
});
}

View file

@ -1922,44 +1922,33 @@
}
},
mark_feed_as_read: function(feed_id) {
mark_feed_as_read: function(feed_id, days_back) {
feed_id = feed_id || this.active_feed;
this.model.mark_feed_as_read([feed_id]);
this.mark_feed_as_read_update_counts(feed_id);
if (feed_id == this.active_feed) {
this.model.stories.each(function(story) {
story.set('read_status', true);
});
var cutoff_timestamp = NEWSBLUR.utils.days_back_to_timestamp(days_back);
if (!days_back && this.model.stories.length &&
this.model.stories.first().get('story_feed_id') == feed_id &&
NEWSBLUR.assets.view_setting(feed_id, 'order') == 'newest') {
cutoff_timestamp = this.model.stories.first().get('story_timestamp');
}
this.model.mark_feed_as_read([feed_id], cutoff_timestamp, feed_id == this.active_feed, _.bind(function() {
this.feeds_unread_count(feed_id);
}, this));
},
mark_folder_as_read: function(folder) {
mark_folder_as_read: function(folder, days_back) {
var folder = folder || this.active_folder;
var feeds = folder.feed_ids_in_folder();
this.model.mark_feed_as_read(feeds);
_.each(feeds, _.bind(function(feed_id) {
this.mark_feed_as_read_update_counts(feed_id);
}, this));
var cutoff_timestamp = NEWSBLUR.utils.days_back_to_timestamp(days_back);
if (!days_back && this.model.stories.length &&
_.contains(feeds, this.model.stories.first().get('story_feed_id')) &&
NEWSBLUR.assets.view_setting(folder.id, 'order') == 'newest') {
cutoff_timestamp = this.model.stories.first().get('story_timestamp');
}
if (folder == this.active_folder) {
this.model.stories.each(function(story) {
story.set('read_status', true);
});
}
},
mark_feed_as_read_update_counts: function(feed_id) {
if (feed_id) {
var feed = this.model.get_feed(feed_id);
if (!feed) return;
feed.set('ps', 0);
feed.set('nt', 0);
feed.set('ng', 0);
}
this.model.mark_feed_as_read(feeds, cutoff_timestamp, folder == this.active_folder, _.bind(function() {
this.feeds_unread_count(feeds);
}, this));
},
open_story_trainer: function(story_id, feed_id, options) {
@ -4150,6 +4139,12 @@
}, this), Math.random() * delay);
},
feeds_unread_count: function(feed_ids, options) {
options = options || {};
this.model.feed_unread_count(feed_ids, options.callback);
},
update_interactions_count: function() {
this.model.interactions_count(function(data) {
NEWSBLUR.app.sidebar_header.update_interactions_count(data.interactions_count);

View file

@ -210,6 +210,13 @@ NEWSBLUR.utils = {
}
return interval;
},
days_back_to_timestamp: function(days_back) {
days_back = days_back || 0;
var now = Math.round((new Date()).getTime() / 1000);
return now - (days_back * 60*60*24);
}

View file

@ -11,6 +11,7 @@ NEWSBLUR.Views.FeedTitleView = Backbone.View.extend({
"dblclick .feed_counts" : "mark_feed_as_read",
"dblclick" : "open_feed_link",
"click .NB-feedbar-mark-feed-read" : "mark_feed_as_read",
"click .NB-feedbar-mark-feed-read-time" : "mark_feed_as_read_days",
"click .NB-feedbar-mark-feed-read-expand" : "expand_mark_read",
"click .NB-feedbar-train-feed" : "open_trainer",
"click .NB-feedbar-statistics" : "open_statistics",
@ -69,10 +70,10 @@ NEWSBLUR.Views.FeedTitleView = Backbone.View.extend({
<% if (type == "story") { %>\
<div class="NB-feedbar-mark-feed-read-container">\
<div class="NB-feedbar-mark-feed-read"><div class="NB-icon"></div></div>\
<div class="NB-feedbar-mark-feed-read-time NB-1d">1d</div>\
<div class="NB-feedbar-mark-feed-read-time NB-3d">3d</div>\
<div class="NB-feedbar-mark-feed-read-time NB-7d">7d</div>\
<div class="NB-feedbar-mark-feed-read-time NB-14d">14d</div>\
<div class="NB-feedbar-mark-feed-read-time" data-days="1">1d</div>\
<div class="NB-feedbar-mark-feed-read-time" data-days="3">3d</div>\
<div class="NB-feedbar-mark-feed-read-time" data-days="7">7d</div>\
<div class="NB-feedbar-mark-feed-read-time" data-days="14">14d</div>\
<div class="NB-feedbar-mark-feed-read-expand"></div>\
</div>\
<div class="NB-story-title-indicator">\
@ -285,7 +286,7 @@ NEWSBLUR.Views.FeedTitleView = Backbone.View.extend({
return false;
},
mark_feed_as_read: function(e) {
mark_feed_as_read: function(e, days) {
if (e) {
e.preventDefault();
e.stopPropagation();
@ -294,13 +295,18 @@ NEWSBLUR.Views.FeedTitleView = Backbone.View.extend({
_.delay(_.bind(function() {
this.flags.double_click = false;
}, this), 500);
NEWSBLUR.reader.mark_feed_as_read(this.model.id);
NEWSBLUR.reader.mark_feed_as_read(this.model.id, days);
this.$('.NB-feedbar-mark-feed-read-container').fadeOut(400);
if (e) {
return false;
}
},
mark_feed_as_read_days: function(e) {
var days = parseInt($(e.target).data('days'), 10);
this.mark_feed_as_read(e, days);
},
expand_mark_read: function() {
var $container = this.$(".NB-feedbar-mark-feed-read-container");
var $markread = this.$(".NB-feedbar-mark-feed-read");

View file

@ -17,6 +17,7 @@ NEWSBLUR.Views.Folder = Backbone.View.extend({
"click .NB-feedlist-collapse-icon" : "collapse_folder",
"click .NB-feedbar-mark-feed-read" : "mark_folder_as_read",
"click .NB-feedbar-mark-feed-read-expand" : "expand_mark_read",
"click .NB-feedbar-mark-feed-read-time" : "mark_folder_as_read_days",
"click .NB-feedbar-options" : "open_options_popover",
"click .NB-story-title-indicator" : "show_hidden_story_titles",
"mouseenter" : "add_hover_inverse",
@ -106,10 +107,10 @@ NEWSBLUR.Views.Folder = Backbone.View.extend({
<% if (feedbar) { %>\
<div class="NB-feedbar-mark-feed-read-container">\
<div class="NB-feedbar-mark-feed-read"><div class="NB-icon"></div></div>\
<div class="NB-feedbar-mark-feed-read-time NB-1d">1d</div>\
<div class="NB-feedbar-mark-feed-read-time NB-3d">3d</div>\
<div class="NB-feedbar-mark-feed-read-time NB-7d">7d</div>\
<div class="NB-feedbar-mark-feed-read-time NB-14d">14d</div>\
<div class="NB-feedbar-mark-feed-read-time" data-days="1">1d</div>\
<div class="NB-feedbar-mark-feed-read-time" data-days="3">3d</div>\
<div class="NB-feedbar-mark-feed-read-time" data-days="7">7d</div>\
<div class="NB-feedbar-mark-feed-read-time" data-days="14">14d</div>\
<div class="NB-feedbar-mark-feed-read-expand"></div>\
</div>\
<div class="NB-story-title-indicator">\
@ -360,10 +361,15 @@ NEWSBLUR.Views.Folder = Backbone.View.extend({
}
},
mark_folder_as_read: function() {
NEWSBLUR.reader.mark_folder_as_read();
mark_folder_as_read: function(days_back) {
NEWSBLUR.reader.mark_folder_as_read(this.model, days_back);
this.$('.NB-feedbar-mark-feed-read').fadeOut(400);
},
mark_folder_as_read_days: function(e) {
var days = parseInt($(e.target).data('days'), 10);
this.mark_folder_as_read(days);
},
expand_mark_read: function() {
NEWSBLUR.Views.FeedTitleView.prototype.expand_mark_read.call(this);