Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
jaesivsm committed Oct 6, 2016
1 parent 7316b29 commit 0c7903d
Show file tree
Hide file tree
Showing 12 changed files with 121 additions and 20 deletions.
1 change: 1 addition & 0 deletions src/tests/ui_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ def test_getclu(self):
resp = self.app.get('/getclu/1',
headers={'Content-Type': 'application/json'})
self.assertEquals(200, resp.status_code)
assert 'notifications' in json.loads(resp.data.decode('utf8'))
self.app.get('/logout')
self.app.post('/login', data={'login': 'user2', 'password': 'user2'})
resp = self.app.get('/getclu/1',
Expand Down
13 changes: 2 additions & 11 deletions src/web/js/actions/MenuActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,8 @@ var MenuActions = {
reload: function(set_filter, setFilterFunc, id) {
jquery.getJSON('/menu', function(payload) {
var old_all_unread_count = MenuStore.all_unread_count;
JarrDispatcher.dispatch({
type: ActionTypes.RELOAD_MENU,
feeds: payload.feeds,
categories: payload.categories,
categories_order: payload.categories_order,
is_admin: payload.is_admin,
max_error: payload.max_error,
error_threshold: payload.error_threshold,
crawling_method: payload.crawling_method,
all_unread_count: payload.all_unread_count,
});
payload.type = ActionTypes.RELOAD_MENU;
JarrDispatcher.dispatch(payload);
/* setfilter param is here so were sure it's called in the sole
* purpose of setting filter and that the setFilterFunc is not
* some event passed by react
Expand Down
1 change: 1 addition & 0 deletions src/web/js/actions/RightPanelActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var RightPanelActions = {
JarrDispatcher.dispatch({
type: ActionTypes.LOAD_CLUSTER,
cluster: payload,
notifications: payload.notifications,
was_read_before: was_read_before,
article_id: article_id,
});
Expand Down
2 changes: 2 additions & 0 deletions src/web/js/components/MainApp.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var JarrNavBar = require('./Navbar.react');
var Menu = require('./Menu.react');
var MiddlePanel = require('./MiddlePanel.react');
var RightPanel = require('./RightPanel.react');
var Notifications = require('./Notifications.react');


var MainApp = React.createClass({
Expand All @@ -16,6 +17,7 @@ var MainApp = React.createClass({
<MiddlePanel />
<RightPanel />
</Grid>
<Notifications />
</div>
);
},
Expand Down
46 changes: 46 additions & 0 deletions src/web/js/components/Notifications.react.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
var React = require('react');
var NotificationsStore = require('../stores/NotificationsStore');
var NotificationSystem = require('react-notification-system');


var Notifications = React.createClass({
_notificationSystem: null,
addNotification: function(notif) {
this._notificationSystem.addNotification({
message: notif.message,
level: notif.level,
autoDismiss: 30,
onRemove: this.removeNotification,
});
},
removeNotification: function(notif) {
for(var idx in NotificationsStore.notifs) {
if(NotificationsStore.notifs[idx].read == false
&& NotificationsStore.notifs[idx].level == notif.level
&& NotificationsStore.notifs[idx].message == notif.message) {
NotificationsStore.notifs[idx].read = true;
break;
}

}
},
render: function() {
return <NotificationSystem ref="notificationSystem" />;
},
componentDidMount: function() {
this._notificationSystem = this.refs.notificationSystem;
NotificationsStore.addChangeListener(this._onChange);
},
componentWillUnmount: function() {
NotificationsStore.removeChangeListener(this._onChange);
},
_onChange: function() {
for(var idx in NotificationsStore.notifs) {
if(!NotificationsStore.notifs[idx].read) {
this.addNotification(NotificationsStore.notifs[idx]);
}
}
},
});

module.exports = Notifications;
50 changes: 50 additions & 0 deletions src/web/js/stores/NotificationsStore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
var JarrDispatcher = require('../dispatcher/JarrDispatcher');
var ActionTypes = require('../constants/JarrConstants');
var EventEmitter = require('events').EventEmitter;

var CHANGE_EVENT = 'change_menu';
var assign = require('object-assign');


var NotificationsStore = assign({}, EventEmitter.prototype, {
notifs: [],

addNotifications: function(notifications) {
var count = this.notifs.length;
for(var idx in notifications) {
this.notifs.push({
key: parseInt(idx) + count,
read: false,
level: notifications[idx].level,
message: notifications[idx].message,
});
}
},
getNotifications: function() {
this.notifs = this.notifs.filter(function(notif) {return !notif.read;});
return this.notifs;
},
emitChange: function(all_folded) {
if (all_folded) {
this.all_folded = all_folded;
} else {
this.all_folded = null;
}
this.emit(CHANGE_EVENT);
},
addChangeListener: function(callback) {
this.on(CHANGE_EVENT, callback);
},
removeChangeListener: function(callback) {
this.removeListener(CHANGE_EVENT, callback);
},
});

NotificationsStore.dispatchToken = JarrDispatcher.register(function(action) {
if(action.notifications) {
NotificationsStore.addNotifications(action.notifications);
NotificationsStore.emitChange();
}
});

module.exports = NotificationsStore;
13 changes: 11 additions & 2 deletions src/web/lib/view_utils.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import pytz
from functools import wraps
from datetime import datetime
from flask import request, Response, make_response
from flask import request, Response, make_response, get_flashed_messages
from flask_babel import get_locale
from babel.dates import format_datetime, format_timedelta
from web.views.common import jsonify
from lib.utils import to_hash

ACCEPTED_LEVELS = {'success', 'info', 'warning', 'error'}


def etag_match(func):
@wraps(func)
Expand Down Expand Up @@ -41,7 +43,14 @@ def _iter_on_rows(rows, now, locale):
yield row


def get_notifications():
for msg in get_flashed_messages(with_categories=True):
yield {'level': msg[0] if msg[0] in ACCEPTED_LEVELS else 'info',
'message': msg[1]}


@jsonify
def clusters_to_json(clusters):
return {'clusters': _iter_on_rows(clusters,
datetime.utcnow(), get_locale())}
datetime.utcnow(), get_locale()),
'notifications': get_notifications()}
4 changes: 2 additions & 2 deletions src/web/views/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def user(user_id=None):
unread_counts=clu_contr.count_by_feed(read=False))

else:
flash(gettext('This user does not exist.'), 'warn')
flash(gettext('This user does not exist.'), 'warning')
return redirect(redirect_url())


Expand All @@ -67,7 +67,7 @@ def toggle_user(user_id=None):
{'is_active': not user.is_active})

if not user_changed:
flash(gettext('This user does not exist.'), 'danger')
flash(gettext('This user does not exist.'), 'error')
return redirect(url_for('admin.dashboard'))

else:
Expand Down
1 change: 0 additions & 1 deletion src/web/views/feed.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import logging
import requests.exceptions
from werkzeug.exceptions import BadRequest

from flask import Blueprint, render_template, flash, \
Expand Down
6 changes: 4 additions & 2 deletions src/web/views/home.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from bootstrap import conf
from web.lib.article_cleaner import clean_urls
from web.lib.view_utils import etag_match, clusters_to_json
from web.lib.view_utils import etag_match, clusters_to_json, get_notifications
from web.views.common import jsonify

from web.controllers import (UserController, CategoryController,
Expand Down Expand Up @@ -71,6 +71,7 @@ def get_menu():
'max_error': conf.FEED_ERROR_MAX,
'error_threshold': conf.FEED_ERROR_THRESHOLD,
'is_admin': current_user.is_admin,
'notifications': get_notifications(),
'all_unread_count': 0}


Expand Down Expand Up @@ -144,7 +145,7 @@ def get_cluster(cluster_id, parse=False, article_id=None):
new_content = clean_urls(new_content, article['link'],
fix_readability=True)
except Exception as error:
flash("Readability failed with %r" % error, "danger")
flash("Readability failed with %r" % error, "warning")
article['readability_parsed'] = False
else:
article['readability_parsed'] = True
Expand All @@ -153,6 +154,7 @@ def get_cluster(cluster_id, parse=False, article_id=None):
{'readability_parsed': True, 'content': new_content})
for article in cluster.articles:
article['readability_available'] = readability_available
cluster['notifications'] = get_notifications()
return cluster


Expand Down
2 changes: 1 addition & 1 deletion src/web/views/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def opml_import():
try:
subscriptions = opml.from_string(data.read())
except:
flash(gettext("Couldn't parse file"), 'danger')
flash(gettext("Couldn't parse file"), 'error')
return redirect(request.referrer)

ccontr = CategoryController(current_user.id)
Expand Down
2 changes: 1 addition & 1 deletion src/web/views/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def authentication_required(error):
def authentication_failed(error):
if conf.API_ROOT in request.url:
return error
flash(gettext('Forbidden.'), 'danger')
flash(gettext('Forbidden.'), 'error')
return redirect(url_for('login'))


Expand Down

0 comments on commit 0c7903d

Please sign in to comment.