Notifications now save their filter and type. Also updates the UI.

This commit is contained in:
Samuel Clay 2016-11-15 18:18:31 -08:00
parent 201b024261
commit fd4db68f7b
9 changed files with 221 additions and 63 deletions

View file

@ -22,6 +22,7 @@ class MUserFeedNotification(mongo.Document):
user_id = mongo.IntField()
feed_id = mongo.IntField()
frequency = mongoengine_fields.IntEnumField(NotificationFrequency)
is_focus = mongo.BooleanField()
last_notification_date = mongo.DateTimeField(default=datetime.datetime.now)
is_email = mongo.BooleanField()
is_web = mongo.BooleanField()
@ -58,12 +59,14 @@ class MUserFeedNotification(mongo.Document):
for feed in notifications:
notifications_by_feed[feed.feed_id] = {
'notifications': [],
'notification_freq': feed.frequency
'notification_types': [],
'notification_filter': "focus" if feed.is_focus else "unread",
}
if feed.is_email: notifications_by_feed[feed.feed_id]['notifications'].append('email')
if feed.is_web: notifications_by_feed[feed.feed_id]['notifications'].append('web')
if feed.is_ios: notifications_by_feed[feed.feed_id]['notifications'].append('ios')
if feed.is_android: notifications_by_feed[feed.feed_id]['notifications'].append('android')
if feed.is_email: notifications_by_feed[feed.feed_id]['notification_types'].append('email')
if feed.is_web: notifications_by_feed[feed.feed_id]['notification_types'].append('web')
if feed.is_ios: notifications_by_feed[feed.feed_id]['notification_types'].append('ios')
if feed.is_android: notifications_by_feed[feed.feed_id]['notification_types'].append('android')
return notifications_by_feed
return notifications_by_feed

View file

@ -3,5 +3,6 @@ from apps.notifications import views
from oauth2_provider import views as op_views
urlpatterns = patterns('',
url(r'^/?$', views.notifications_by_feed, name='notifications-by-feed'),
url(r'^$', views.notifications_by_feed, name='notifications-by-feed'),
url(r'^feed/?$', views.set_notifications_for_feed, name='set-notifications-for-feed'),
)

View file

@ -10,4 +10,38 @@ def notifications_by_feed(request):
user = get_user(request)
notifications_by_feed = MUserFeedNotification.feeds_for_user(user.pk)
return notifications_by_feed
return notifications_by_feed
@ajax_login_required
@json.json_view
def set_notifications_for_feed(request):
user = get_user(request)
feed_id = request.POST['feed_id']
notification_types = request.POST.getlist('notification_types')
notification_filter = request.POST.get('notification_filter')
try:
notification = MUserFeedNotification.objects.get(user_id=user.pk, feed_id=feed_id)
except MUserFeedNotification.DoesNotExist:
params = {
"user_id": user.pk,
"feed_id": feed_id,
}
notification = MUserFeedNotification.objects.create(**params)
notification.is_focus = bool(notification_filter == "focus")
notification.is_email = bool('email' in notification_types)
notification.is_ios = bool('ios' in notification_types)
notification.is_android = bool('android' in notification_types)
notification.is_web = bool('web' in notification_types)
notification.save()
if (not notification.is_email and
not notification.is_ios and
not notification.is_android and
not notification.is_web):
notification.delete()
notifications_by_feed = MUserFeedNotification.feeds_for_user(user.pk)
return {"notifications_by_feed": notifications_by_feed}

View file

@ -8821,6 +8821,10 @@ form.opml_import_form input {
/* = Notifications Modal = */
/* ======================= */
.NB-modal-notifications .NB-modal-title .NB-icon {
background: transparent url('/media/embed/icons/circular/menu_icn_notifications_128.png');
background-size: 28px;
}
.NB-modal.NB-modal-notifications .NB-fieldset {
border-bottom: none;
width: 100%;
@ -8836,21 +8840,54 @@ form.opml_import_form input {
float: left;
overflow: hidden;
}
.NB-modal-notifications .NB-modal-section-site {
margin: 12px 0 0;
}
.NB-feed-notification {
width: 100%;
overflow: hidden;
position: relative;
padding: 10px 0;
border-bottom: 1px solid #F0F0F0;
}
.NB-feed-notification .NB-feed-notification-filter {
float: right;
margin: 0 12px;
.NB-feed-notification:last-child {
border-bottom: none;
}
.NB-feed-notification .NB-feed-title {
font-size: 12px;
padding: 0 260px 0 24px;
}
.NB-feed-notification .NB-feed-icon {
width: 16px;
height: 16px;
position: absolute;
top: 10px;
left: 0;
}
.NB-feed-notification .NB-feed-frequency-icon {
float: left;
margin: 0 7px 0 0;
clear: left;
width: 16px;
height: 16px;
margin: 4px 7px 0 0;
}
.NB-feed-notification .NB-feed-frequency {
float: left;
color: #A0A0A0;
font-size: 10px;
margin: 2px 0 0 0;
}
.NB-feed-notification .NB-feed-notification-controls {
position: relative;
top: 0;
right: 0;
}
.NB-feed-notification .NB-feed-notification-filter {
float: right;
margin: 0 12px;
}
.NB-feed-notification .NB-feed-notification-filter .NB-unread-icon,
.NB-feed-notification .NB-feed-notification-filter .NB-focus-icon {
@ -8867,21 +8904,24 @@ form.opml_import_form input {
background-size: 8px;
}
.NB-feed-notification .segmented-control {
margin-top: -2px;
margin-bottom: 2px;
margin: 0 0 4px 0;
}
.NB-feed-notification .segmented-control li {
padding: 2px 6px;
font-size: 10px;
min-width: 36px;
}
.NB-feed-notification .NB-feed-notifications {
.NB-feed-notification .segmented-control li:not(.NB-active) {
color: #A0A0A0;
}
.NB-feed-notification .NB-feed-notification-types {
float: right;
clear: right;
}
.NB-modal-notifications .NB-modal-section-site {
margin: 12px 0 0;
.NB-feed-notification .NB-feed-notification-filter-focus {
width: 82px;
}
/* ===================== */
/* = Feedchooser Modal = */
/* ===================== */

View file

@ -1482,6 +1482,18 @@ NEWSBLUR.AssetModel = Backbone.Router.extend({
}
},
set_notifications_for_feed: function(feed, callback) {
if (NEWSBLUR.Globals.is_authenticated) {
this.make_request('/notifications/feed/', {
'feed_id': feed.id,
'notification_types': feed.get('notification_types'),
'notification_filter': feed.get('notification_filter')
}, callback);
} else {
if ($.isFunction(callback)) callback();
}
},
send_story_email: function(data, callback, error_callback) {
if (NEWSBLUR.Globals.is_authenticated) {
this.make_request('/reader/send_story_email', data, callback, error_callback, {'timeout': 6000});

View file

@ -271,6 +271,8 @@ NEWSBLUR.Collections.Feeds = Backbone.Collection.extend({
this.bind('change', this.detect_active_feed);
},
comparator: 'feed_title',
// ===========
// = Actions =
// ===========

View file

@ -34,9 +34,13 @@ _.extend(NEWSBLUR.ReaderNotifications.prototype, {
var frequency = this.feed.get('notification_frequency');
var notifications = this.feed.get('notifications');
if (this.feed) {
NEWSBLUR.Modal.prototype.initialize_feed.call(this, feed_id);
}
NEWSBLUR.Modal.prototype.initialize_feed.call(this, feed_id);
var $site = $(".NB-modal-section-site", this.$modal);
$site.html(this.make_feed_notification(this.feed));
var $all = $(".NB-modal-section-all", this.$modal);
$all.html(this.make_feed_notifications());
this.resize();
},
@ -68,7 +72,12 @@ _.extend(NEWSBLUR.ReaderNotifications.prototype, {
this.make_feed_chooser()
])),
$.make('div', { className: 'NB-modal-loading' }),
$.make('h2', { className: 'NB-modal-title' }, 'Notifications'),
$.make('h2', { className: 'NB-modal-title' }, [
$.make('div', { className: 'NB-modal-loading' }),
$.make('div', { className: 'NB-icon' }),
'Notificatons',
$.make('div', { className: 'NB-icon-dropdown' })
]),
(this.feed && $.make('div', { className: 'NB-fieldset NB-modal-submit' }, [
$.make('fieldset', [
$.make('legend', 'Site Notifications'),
@ -80,7 +89,7 @@ _.extend(NEWSBLUR.ReaderNotifications.prototype, {
$.make('div', { className: 'NB-fieldset NB-modal-submit' }, [
$.make('fieldset', [
$.make('legend', 'All Notifications'),
$.make('div', { className: 'NB-modal-section'}, [
$.make('div', { className: 'NB-modal-section NB-modal-section-all'}, [
this.make_feed_notifications()
])
])
@ -104,13 +113,15 @@ _.extend(NEWSBLUR.ReaderNotifications.prototype, {
},
make_feed_notifications: function() {
var notifications = this.model.get_feeds().filter(function(feed) {
return feed.get('notifications');
var site_feed_id = this.model.id;
var notifications = this.model.get_feeds().select(function(feed) {
return feed.get('notification_types') && feed.id != site_feed_id;
});
var $feeds = [];
notifications.sort(function(a, b) { return a.get('feed_title') < b.get('feed_title'); });
for (var feed in notifications) {
$feeds.push(this.make_feed_notification(feed));
$feeds.push(this.make_feed_notification(notifications[feed]));
}
return $feeds;

View file

@ -1,62 +1,72 @@
NEWSBLUR.Views.FeedNotificationView = Backbone.View.extend({
events: {
"click .NB-feed-notifications-email" : "toggle_email",
"click .NB-feed-notifications-ios" : "toggle_ios",
"click .NB-feed-notifications-android" : "toggle_android",
"click .NB-feed-notifications-web" : "toggle_web"
"click .NB-feed-notification-filter-unread": "toggle_unread",
"click .NB-feed-notification-filter-focus" : "toggle_focus",
"click .NB-feed-notification-email" : "toggle_email",
"click .NB-feed-notification-ios" : "toggle_ios",
"click .NB-feed-notification-android" : "toggle_android",
"click .NB-feed-notification-web" : "toggle_web"
},
initialize: function() {
initialize: function(m) {
console.log(['feed notificaiton', this.model, m]);
},
render: function() {
var feed = this.model;
var $feed = $(_.template('<div class="NB-feed-notification <% if (selected) { %>selected<% } %>">\
<ul class="segmented-control NB-feed-notifications">\
<li class="NB-feed-notifications-option NB-feed-notifications-email">Email</li>\
<li class="NB-feed-notifications-option NB-feed-notifications-web">Web</li>\
<li class="NB-feed-notifications-option NB-feed-notifications-ios">iOS</li>\
<li class="NB-feed-notifications-option NB-feed-notifications-android">Android</li>\
</ul>\
<ul class="segmented-control NB-feed-notification-filter">\
<li class="NB-feed-notification-filter-unread NB-active">\
<div class="NB-unread-icon"></div>\
Unread stories\
</li>\
<li class="NB-feed-notification-filter-focus">\
<div class="NB-focus-icon"></div>\
Focus\
</li>\
</ul>\
<div class="NB-feed-notification-controls">\
<ul class="segmented-control NB-feed-notification-filter">\
<li class="NB-feed-notification-filter-unread <% if (!is_focus) { %>NB-active<% } %>">\
<div class="NB-unread-icon"></div>\
Unread stories\
</li>\
<li class="NB-feed-notification-filter-focus <% if (is_focus) { %>NB-active<% } %>">\
<div class="NB-focus-icon"></div>\
Focus\
</li>\
</ul>\
<ul class="segmented-control NB-feed-notification-types">\
<li class="NB-feed-notification-option NB-feed-notification-email <% if (is_email) { %>NB-active<% } %>">Email</li>\
<li class="NB-feed-notification-option NB-feed-notification-web <% if (is_web) { %>NB-active<% } %>">Web</li>\
<li class="NB-feed-notification-option NB-feed-notification-ios <% if (is_ios) { %>NB-active<% } %>">iOS</li>\
<li class="NB-feed-notification-option NB-feed-notification-android <% if (is_android) { %>NB-active<% } %>">Android</li>\
</ul>\
</div>\
<img class="NB-feed-icon" src="<%= $.favicon(feed) %>">\
<div class="NB-feed-title"><%= feed.get("feed_title") %></div>\
<div class="NB-feed-frequency-icon"></div>\
<div class="NB-feed-frequency"><%= frequency %></div>\
</div>\
', {
feed : feed,
selected : this.options.selected,
frequency_count : this.frequency_count()
frequency : feed && this.frequency(feed.get('average_stories_per_month')),
frequency_count : this.frequency_count(),
is_email : _.contains(feed.get('notification_types'), 'email'),
is_ios : _.contains(feed.get('notification_types'), 'ios'),
is_android : _.contains(feed.get('notification_types'), 'android'),
is_web : _.contains(feed.get('notification_types'), 'web'),
is_focus : feed.get('notification_filter') == 'focus'
}));
// <div class="NB-feed-notification-frequency">\
// <select name="notifications_frequency">\
// <option value="0">Immediately</option>\
// <option value="1">Max once an hour</option>\
// <option value="1">Max once every six hours</option>\
// <option value="1">Max once every twelve hours</option>\
// <option value="1">Max once a day</option>\
// </select>\
// </div>\
// <div class="NB-feed-notification-frequency-count">\
// <%= frequency_count %> a day\
// </div>\
this.$el.replaceWith($feed);
this.setElement($feed);
return this;
},
frequency: function(count) {
if (count == 0) {
return "No stories published last month";
} else if (count < 30) {
return Inflector.pluralize("story", count, true) + " per month";
} else if (count >= 30) {
return Inflector.pluralize("story", Math.round(count / 30.0), true) + " per day";
}
},
frequency_count: function() {
var freq = this.model.get('notification_frequency');
var story_count = this.model.get('stories_per_month') / 30.0;
@ -76,7 +86,52 @@ NEWSBLUR.Views.FeedNotificationView = Backbone.View.extend({
// ==========
toggle_email: function() {
this.toggle_type('email');
},
toggle_ios: function() {
this.toggle_type('ios');
},
toggle_android: function() {
this.toggle_type('android');
},
toggle_web: function() {
this.toggle_type('web');
},
toggle_type: function(type) {
var notification_types = this.model.get('notification_types') || [];
var is_type = _.contains(notification_types, type);
if (is_type) {
notification_types.splice(notification_types.indexOf(type), 1);
} else {
notification_types.push(type);
}
this.model.set('notification_types', notification_types);
this.save();
},
toggle_focus: function() {
this.model.set('notification_filter', 'focus');
this.save();
},
toggle_unread: function() {
this.model.set('notification_filter', 'unread');
this.save();
},
save: function() {
NEWSBLUR.assets.set_notifications_for_feed(this.model, function() {
});
this.render();
}
});

View file

@ -30,7 +30,7 @@ urlpatterns = patterns('',
(r'^import/', include('apps.feed_import.urls')),
(r'^api/', include('apps.api.urls')),
(r'^recommendations/', include('apps.recommendations.urls')),
(r'^notifications/?', include('apps.notifications.urls')),
(r'^notifications/?', include('apps.notifications.urls')),
(r'^statistics/', include('apps.statistics.urls')),
(r'^social/', include('apps.social.urls')),
(r'^oauth/', include('apps.oauth.urls')),