aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/web/js/actions/MenuActions.js11
-rw-r--r--src/web/js/actions/MiddlePanelActions.js57
-rw-r--r--src/web/js/components/MainApp.react.js21
-rw-r--r--src/web/js/components/Menu.react.js143
-rw-r--r--src/web/js/components/MiddlePanel.react.js111
-rw-r--r--src/web/js/constants/JarrConstants.js8
-rw-r--r--src/web/js/stores/MenuStore.js24
-rw-r--r--src/web/js/stores/MiddlePanelStore.js19
-rw-r--r--src/web/views/views.py2
9 files changed, 273 insertions, 123 deletions
diff --git a/src/web/js/actions/MenuActions.js b/src/web/js/actions/MenuActions.js
index c3cc95bc..1f1eea01 100644
--- a/src/web/js/actions/MenuActions.js
+++ b/src/web/js/actions/MenuActions.js
@@ -17,19 +17,20 @@ var MenuActions = {
},
setFilterAll: function() {
JarrDispatcher.dispatch({
- component: 'menu',
- type: MenuActionTypes.MENU_FILTER_ALL,
+ type: MenuActionTypes.MENU_FILTER,
+ filter: 'all',
});
},
setFilterUnread: function() {
JarrDispatcher.dispatch({
- component: 'menu',
- type: MenuActionTypes.MENU_FILTER_UNREAD,
+ type: MenuActionTypes.MENU_FILTER,
+ filter: 'unread',
});
},
setFilterError: function() {
JarrDispatcher.dispatch({
- type: MenuActionTypes.MENU_FILTER_ERROR,
+ type: MenuActionTypes.MENU_FILTER,
+ filter: 'error',
});
},
diff --git a/src/web/js/actions/MiddlePanelActions.js b/src/web/js/actions/MiddlePanelActions.js
index 159ba91b..7a944ecd 100644
--- a/src/web/js/actions/MiddlePanelActions.js
+++ b/src/web/js/actions/MiddlePanelActions.js
@@ -28,10 +28,11 @@ var reloadIfNecessaryAndDispatch = function(dispath_payload) {
filters[key] = dispath_payload[key];
}
}
- jquery.getJSON('/middle_panel', dispath_payload, function(payload) {
- dispath_payload.articles = payload.articles;
- JarrDispatcher.dispatch(dispath_payload);
- _last_fetched_with = MiddlePanelStore.getRequestFilter();
+ jquery.getJSON('/middle_panel', dispath_payload,
+ function(payload) {
+ dispath_payload.articles = payload.articles;
+ JarrDispatcher.dispatch(dispath_payload);
+ _last_fetched_with = MiddlePanelStore.getRequestFilter();
});
} else {
JarrDispatcher.dispatch(dispath_payload);
@@ -50,24 +51,24 @@ var MiddlePanelActions = {
});
});
},
- removeParentFilter: function(filter_type, filter_id) {
+ removeParentFilter: function() {
reloadIfNecessaryAndDispatch({
- type: MiddlePanelActionTypes.MIDDLE_PANEL_PARENT_FILTER,
+ type: MiddlePanelActionTypes.PARENT_FILTER,
filter_type: null,
filter_id: null,
});
},
setCategoryFilter: function(category_id) {
reloadIfNecessaryAndDispatch({
- type: MiddlePanelActionTypes.MIDDLE_PANEL_PARENT_FILTER,
- filter_type: 'category',
+ type: MiddlePanelActionTypes.PARENT_FILTER,
+ filter_type: 'category_id',
filter_id: category_id,
});
},
setFeedFilter: function(feed_id) {
reloadIfNecessaryAndDispatch({
- type: MiddlePanelActionTypes.MIDDLE_PANEL_PARENT_FILTER,
- filter_type: 'feed',
+ type: MiddlePanelActionTypes.PARENT_FILTER,
+ filter_type: 'feed_id',
filter_id: feed_id,
});
},
@@ -89,6 +90,42 @@ var MiddlePanelActions = {
filter: 'liked',
});
},
+ changeRead: function(article_id, new_value){
+ jquery.ajax({type: 'PUT',
+ contentType: 'application/json',
+ data: JSON.stringify({readed: new_value}),
+ url: "api/v2.0/article/" + article_id,
+ success: function (result) {
+ JarrDispatcher.dispatch({
+ type: MiddlePanelActionTypes.CHANGE_ATTR,
+ article_id: article_id,
+ attribute: 'read',
+ value: new_value,
+ });
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown){
+ console.log(XMLHttpRequest.responseText);
+ },
+ });
+ },
+ changeLike: function(article_id, new_value){
+ jquery.ajax({type: 'PUT',
+ contentType: 'application/json',
+ data: JSON.stringify({like: new_value}),
+ url: "api/v2.0/article/" + article_id,
+ success: function (result) {
+ JarrDispatcher.dispatch({
+ type: MiddlePanelActionTypes.CHANGE_ATTR,
+ article_id: article_id,
+ attribute: 'liked',
+ value: new_value,
+ });
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown){
+ console.log(XMLHttpRequest.responseText);
+ },
+ });
+ },
};
module.exports = MiddlePanelActions;
diff --git a/src/web/js/components/MainApp.react.js b/src/web/js/components/MainApp.react.js
index 743c9510..059d2646 100644
--- a/src/web/js/components/MainApp.react.js
+++ b/src/web/js/components/MainApp.react.js
@@ -1,16 +1,23 @@
+var React = require('react');
+var Col = require('react-bootstrap/lib/Col');
+var Grid = require('react-bootstrap/lib/Grid');
+
var Menu = require('./Menu.react');
var MiddlePanel = require('./MiddlePanel.react');
-var React = require('react');
var MainApp = React.createClass({
render: function() {
- return (<div className="container-fluid">
- <div className="row row-offcanvas row-offcanvas-left">
- <Menu />
- <MiddlePanel />
- </div>
- </div>
+ return (<Grid fluid>
+ <Col xsHidden smHidden md={3} lg={2}>
+ <Menu.MenuFilter />
+ <Menu.Menu />
+ </Col>
+ <Col xs={2} sm={2} md={4} lg={4}>
+ <MiddlePanel.MiddlePanelFilter />
+ <MiddlePanel.MiddlePanel />
+ </Col>
+ </Grid>
);
},
});
diff --git a/src/web/js/components/Menu.react.js b/src/web/js/components/Menu.react.js
index d44e7bd3..3e5f4156 100644
--- a/src/web/js/components/Menu.react.js
+++ b/src/web/js/components/Menu.react.js
@@ -1,7 +1,11 @@
var React = require('react');
+var Row = require('react-bootstrap/lib/Row');
+var Badge = require('react-bootstrap/lib/Badge');
var Button = require('react-bootstrap/lib/Button');
var ButtonGroup = require('react-bootstrap/lib/ButtonGroup');
-var Badge = require('react-bootstrap/lib/Badge');
+var ListGroup = require('react-bootstrap/lib/ListGroup');
+var ListGroupItem = require('react-bootstrap/lib/ListGroupItem');
+var Glyphicon = require('react-bootstrap/lib/Glyphicon');
var MenuStore = require('../stores/MenuStore');
var MenuActions = require('../actions/MenuActions');
@@ -13,27 +17,29 @@ var FeedItem = React.createClass({
unread: React.PropTypes.number.isRequired,
error_count: React.PropTypes.number.isRequired,
icon_url: React.PropTypes.string,
+ active: React.PropTypes.bool.isRequired,
},
render: function() {
var icon = null;
var badge_unread = null;
- var badge_error = null;
+ var style = null;
if(this.props.icon_url){
icon = (<img width="16px" src={this.props.icon_url} />);
} else {
- icon = (<span className="glyphicon glyphicon-ban-circle" />);
+ icon = <Glyphicon glyph="ban-circle" />;
}
if(this.props.unread){
badge_unread = <Badge pullRight>{this.props.unread}</Badge>;
}
if(this.props.error_count == 6) {
- badge_unread = <Badge pullRight error>error</Badge>;
+ style = "danger";
} else if(this.props.error_count > 3) {
- badge_unread = <Badge pullRight warning>warn</Badge>;
+ style = "warning";
}
- return (<li onMouseDown={this.handleClick}>
- {icon}{this.props.title}{badge_unread}{badge_error}
- </li>
+ return (<ListGroupItem onMouseDown={this.handleClick} bsStyle={style}
+ href="#" active={this.props.active}>
+ {icon}{this.props.title}{badge_unread}
+ </ListGroupItem>
);
},
handleClick: function() {
@@ -42,53 +48,76 @@ var FeedItem = React.createClass({
});
var Category = React.createClass({
- propTypes: {category_id: React.PropTypes.number.isRequired,
+ propTypes: {cat_id: React.PropTypes.number.isRequired,
filter: React.PropTypes.string.isRequired,
+ active_type: React.PropTypes.string,
+ active_id: React.PropTypes.number,
name: React.PropTypes.string.isRequired,
feeds: React.PropTypes.array.isRequired,
unread: React.PropTypes.number.isRequired,
},
+ getInitialState: function() {
+ return {unfolded: true};
+ },
render: function() {
var filter = this.props.filter;
+ var a_type = this.props.active_type;
+ var a_id = this.props.active_id;
// filtering according to this.props.filter
+ if(this.state.unfolded) {
var feeds = this.props.feeds.filter(function(feed) {
- if (filter == 'unread' && feed.unread <= 0) {return false;}
- else if (filter == 'error' && feed.error_count <= 3){return false;}
+ if (filter == 'unread' && feed.unread <= 0) {
+ return false;
+ } else if (filter == 'error' && feed.error_count <= 3) {
+ return false;
+ }
return true;
}).sort(function(feed_a, feed_b){
return feed_b.unread - feed_a.unread;
}).map(function(feed) {
- return (<FeedItem key={"feed" + feed.id} feed_id={feed.id}
+ return (<FeedItem key={"f" + feed.id} feed_id={feed.id}
title={feed.title} unread={feed.unread}
error_count={feed.error_count}
- icon_url={feed.icon_url} />);
+ active={a_type == 'feed_id' && a_id == feed.id}
+ icon_url={feed.icon_url} />
+ );
});
- var unread = undefined;
+ } else {
+ var feeds = [];
+ }
+ var unread = null;
if(this.props.unread){
- unread = (<span className="badge pull-right">
- {this.props.unread}
- </span>);
+ unread = <Badge pullRight>{this.props.unread}</Badge>;
}
- return (<div>
- <h3 onMouseDown={this.handleClick}>
- {this.props.name} {unread}
- </h3>
- <ul className="nav nav-sidebar">{feeds}</ul>
- </div>
+ var active = a_type == 'category_id' && a_id == this.props.cat_id;
+ var ctrl = (<Glyphicon onMouseDown={this.toggleFolding} pullLeft
+ glyph={this.state.unfolded?"menu-down":"menu-right"} />
+ );
+
+ return (<ListGroup>
+ <ListGroupItem href="#" bsStyle="success"
+ active={active}
+ onMouseDown={this.handleClick}>
+ {ctrl} <strong>{this.props.name}</strong> {unread}
+ </ListGroupItem>
+ {feeds}
+ </ListGroup>
);
},
handleClick: function() {
- MiddlePanelActions.setCategoryFilter(this.props.category_id);
+ MiddlePanelActions.setCategoryFilter(this.props.cat_id);
+ },
+ toggleFolding: function(evnt) {
+ this.setState({unfolded: !this.state.unfolded});
+ evnt.stopPropagation();
},
});
-var Menu = React.createClass({
+var MenuFilter = React.createClass({
getInitialState: function() {
- return {filter: 'all', categories: [],
- feed_in_error: false, all_unread_count: 0};
+ return {filter: 'all', feed_in_error: false};
},
render: function() {
- var filter = this.state.filter;
var error_button = null;
if (this.state.feed_in_error) {
error_button = (<Button active={this.state.filter == "error"}
@@ -96,8 +125,7 @@ var Menu = React.createClass({
bsSize="small" bsStyle="warning">Error</Button>
);
}
- return (<div id="sidebar" data-spy="affix" role="navigation"
- className="col-md-2 sidebar sidebar-offcanvas pre-scrollable hidden-sm hidden-xs affix">
+ return (<Row className="show-grid">
<ButtonGroup>
<Button active={this.state.filter == "all"}
onMouseDown={MenuActions.setFilterAll}
@@ -106,17 +134,49 @@ var Menu = React.createClass({
onMouseDown={MenuActions.setFilterUnread}
bsSize="small">Unread</Button>
{error_button}
- </ButtonGroup>
+ </ButtonGroup>
+ </Row>
+ );
+ },
+ componentDidMount: function() {
+ MenuStore.addChangeListener(this._onChange);
+ },
+ componentWillUnmount: function() {
+ MenuStore.removeChangeListener(this._onChange);
+ },
+ _onChange: function() {
+ var datas = MenuStore.getAll();
+ this.setState({filter: datas.filter,
+ feed_in_error: datas.feed_in_error});
+ },
+});
+
+var Menu = React.createClass({
+ getInitialState: function() {
+ return {filter: 'all', categories: [], all_unread_count: 0,
+ active_type: null, active_id: null};
+ },
+ render: function() {
+ var state = this.state;
+ var rmPrntFilt = (<ListGroupItem href="#" bsStyle="success"
+ active={this.state.active_type == null
+ || this.state.active_id == null}
+ onMouseDown={MiddlePanelActions.removeParentFilter}
+ header="All"></ListGroupItem>);
+
+ return (<Row className="show-grid">
+ <ListGroup>{rmPrntFilt}</ListGroup>
{this.state.categories.map(function(category){
- return (<Category key={"cat" + category.id}
- filter={filter}
- category_id={category.id}
- feeds={category.feeds}
- name={category.name}
- unread={category.unread} />);
+ return (<Category key={"c" + category.id}
+ filter={state.filter}
+ active_type={state.active_type}
+ active_id={state.active_id}
+ cat_id={category.id}
+ feeds={category.feeds}
+ name={category.name}
+ unread={category.unread} />);
})}
-
- </div>
+ </Row>
);
},
componentDidMount: function() {
@@ -130,9 +190,10 @@ var Menu = React.createClass({
var datas = MenuStore.getAll();
this.setState({filter: datas.filter,
categories: datas.categories,
- feed_in_error: datas.feed_in_error,
+ active_type: datas.active_type,
+ active_id: datas.active_id,
all_unread_count: datas.all_unread_count});
},
});
-module.exports = Menu;
+module.exports = {Menu: Menu, MenuFilter: MenuFilter};
diff --git a/src/web/js/components/MiddlePanel.react.js b/src/web/js/components/MiddlePanel.react.js
index f95554b8..49365a6a 100644
--- a/src/web/js/components/MiddlePanel.react.js
+++ b/src/web/js/components/MiddlePanel.react.js
@@ -1,6 +1,10 @@
var React = require('react');
+var Row = require('react-bootstrap/lib/Row');
var Button = require('react-bootstrap/lib/Button');
var ButtonGroup = require('react-bootstrap/lib/ButtonGroup');
+var ListGroup = require('react-bootstrap/lib/ListGroup');
+var ListGroupItem = require('react-bootstrap/lib/ListGroupItem');
+var Glyphicon = require('react-bootstrap/lib/Glyphicon');
var MiddlePanelStore = require('../stores/MiddlePanelStore');
var MiddlePanelActions = require('../actions/MiddlePanelActions');
@@ -18,54 +22,78 @@ var TableLine = React.createClass({
return {read: this.props.read, liked: this.props.liked};
},
render: function() {
- var read = this.state.read ? 'r' : '';
var liked = this.state.liked ? 'l' : '';
var icon = null;
if(this.props.icon_url){
icon = (<img width="16px" src={this.props.icon_url} />);
} else {
- icon = (<span className="glyphicon glyphicon-ban-circle" />);
+ icon = <Glyphicon glyph="ban-circle" />;
}
+ var title = (<a href={'/redirect/' + this.props.article_id}>
+ {icon} {this.props.feed_title}
+ </a>);
+ var read = (<Glyphicon glyph={this.state.read?"check":"unchecked"}
+ onClick={this.toogleRead} />);
+ var liked = (<Glyphicon glyph={this.state.liked?"star":"star-empty"}
+ onClick={this.toogleLike} />);
return (
- <tr>
- <td>{icon}{liked}</td>
- <td>
- <a href={'/redirect/' + this.props.article_id}>
- {this.props.feed_title}
- </a>
- </td>
- <td>
- <a href={'/article/' + this.props.article_id}>
- {this.props.title}
- </a>
- </td>
- <td>{this.props.date}</td>
- </tr>
+ <ListGroupItem header={title}>
+ {read}
+ {liked}
+ {this.props.title}
+ </ListGroupItem>
);
},
+ toogleRead: function() {
+ this.setState({read: !this.state.read});
+ MiddlePanelActions.changeRead(this.props.article_id, !this.state.read);
+ },
+ toogleLike: function() {
+ this.setState({liked: !this.state.liked});
+ MiddlePanelActions.changeLike(this.props.article_id, !this.state.liked);
+ },
+});
+
+var MiddlePanelFilter = React.createClass({
+ getInitialState: function() {
+ return {filter: MiddlePanelStore._datas.filter};
+ },
+ render: function() {
+ return (<Row className="show-grid">
+ <ButtonGroup>
+ <Button active={this.state.filter == "all"}
+ onMouseDown={MiddlePanelActions.setFilterAll}
+ bsSize="small">All</Button>
+ <Button active={this.state.filter == "unread"}
+ onMouseDown={MiddlePanelActions.setFilterUnread}
+ bsSize="small">Unread</Button>
+ <Button active={this.state.filter == "liked"}
+ onMouseDown={MiddlePanelActions.setFilterLiked}
+ bsSize="small">Liked</Button>
+ </ButtonGroup>
+ </Row>
+ );
+ },
+ componentDidMount: function() {
+ MiddlePanelStore.addChangeListener(this._onChange);
+ },
+ componentWillUnmount: function() {
+ MiddlePanelStore.removeChangeListener(this._onChange);
+ },
+ _onChange: function() {
+ this.setState({filter: MiddlePanelStore._datas.filter});
+ },
});
-var TableBody = React.createClass({
+var MiddlePanel = React.createClass({
getInitialState: function() {
- return {filter: 'unread', articles: []};
+ return {filter: MiddlePanelStore._datas.filter, articles: []};
},
render: function() {
- return (<div className="table-responsive">
- <ButtonGroup>
- <Button active={this.state.filter == "all"}
- onMouseDown={MiddlePanelActions.setFilterAll}
- bsSize="small">All</Button>
- <Button active={this.state.filter == "unread"}
- onMouseDown={MiddlePanelActions.setFilterUnread}
- bsSize="small">Unread</Button>
- <Button active={this.state.filter == "liked"}
- onMouseDown={MiddlePanelActions.setFilterLiked}
- bsSize="small">Liked</Button>
- </ButtonGroup>
- <table className="table table-striped strict-table">
- <tbody>
+ return (<Row className="show-grid">
+ <ListGroup>
{this.state.articles.map(function(article){
- return (<TableLine key={"article" + article.article_id}
+ return (<TableLine key={"a" + article.article_id}
title={article.title}
icon_url={article.icon_url}
read={article.read}
@@ -73,9 +101,8 @@ var TableBody = React.createClass({
date={article.date}
article_id={article.article_id}
feed_title={article.feed_title} />);})}
- </tbody>
- </table>
- </div>
+ </ListGroup>
+ </Row>
);
},
componentDidMount: function() {
@@ -91,13 +118,5 @@ var TableBody = React.createClass({
},
});
-var MiddlePanel = React.createClass({
- render: function() {
- return (<div className="col-md-offset-2 col-md-10 main">
- <TableBody />
- </div>
- );
- },
-});
-
-module.exports = MiddlePanel;
+module.exports = {MiddlePanel: MiddlePanel,
+ MiddlePanelFilter: MiddlePanelFilter};
diff --git a/src/web/js/constants/JarrConstants.js b/src/web/js/constants/JarrConstants.js
index 3d334ee3..d00f24a8 100644
--- a/src/web/js/constants/JarrConstants.js
+++ b/src/web/js/constants/JarrConstants.js
@@ -14,13 +14,13 @@ var keyMirror = require('keymirror');
module.exports = {
MenuActionTypes: keyMirror({
RELOAD_MENU: null,
- MENU_FILTER_ALL: null,
- MENU_FILTER_UNREAD: null,
- MENU_FILTER_ERROR: null,
+ PARENT_FILTER: null,
+ MENU_FILTER: null,
}),
MiddlePanelActionTypes: keyMirror({
+ PARENT_FILTER: null,
RELOAD_MIDDLE_PANEL: null,
- MIDDLE_PANEL_PARENT_FILTER: null,
MIDDLE_PANEL_FILTER: null,
+ CHANGE_ATTR: null,
}),
};
diff --git a/src/web/js/stores/MenuStore.js b/src/web/js/stores/MenuStore.js
index 016b33f2..6809d8b0 100644
--- a/src/web/js/stores/MenuStore.js
+++ b/src/web/js/stores/MenuStore.js
@@ -6,7 +6,7 @@ var assign = require('object-assign');
var MenuStore = assign({}, EventEmitter.prototype, {
- _datas: {filter: 'all', categories: [],
+ _datas: {filter: 'all', categories: [], active_type: null, active_id: null,
all_unread_count: 0, feed_in_error: false},
getAll: function() {
return this._datas;
@@ -17,6 +17,13 @@ var MenuStore = assign({}, EventEmitter.prototype, {
this.emitChange();
}
},
+ setActive: function(type, value) {
+ if(this._datas.active_id != value || this._datas.active_type != type) {
+ this._datas.active_type = type;
+ this._datas.active_id = value;
+ this.emitChange();
+ }
+ },
readFeedArticle: function(feed_id) {
// TODO
},
@@ -40,14 +47,17 @@ MenuStore.dispatchToken = JarrDispatcher.register(function(action) {
MenuStore._datas['all_unread_count'] = action.all_unread_count;
MenuStore.emitChange();
break;
- case MenuActionTypes.MENU_FILTER_ALL:
- MenuStore.setFilter('all');
+ case MenuActionTypes.PARENT_FILTER:
+ MenuStore.setActive(action.filter_type, action.filter_id);
+ break;
+ case MenuActionTypes.MENU_FILTER:
+ MenuStore.setFilter(action.filter);
break;
- case MenuActionTypes.MENU_FILTER_UNREAD:
- MenuStore.setFilter('unread');
+ case MenuActionTypes.MENU_FILTER:
+ MenuStore.setFilter(action.filter);
break;
- case MenuActionTypes.MENU_FILTER_ERROR:
- MenuStore.setFilter('error');
+ case MenuActionTypes.MENU_FILTER:
+ MenuStore.setFilter(action.filter);
break;
default:
diff --git a/src/web/js/stores/MiddlePanelStore.js b/src/web/js/stores/MiddlePanelStore.js
index 80ac8198..201bebd1 100644
--- a/src/web/js/stores/MiddlePanelStore.js
+++ b/src/web/js/stores/MiddlePanelStore.js
@@ -1,5 +1,6 @@
var JarrDispatcher = require('../dispatcher/JarrDispatcher');
var MiddlePanelActionTypes = require('../constants/JarrConstants').MiddlePanelActionTypes;
+var MenuActionTypes = require('../constants/JarrConstants').MenuActionTypes;
var EventEmitter = require('events').EventEmitter;
var CHANGE_EVENT = 'change_middle_panel';
var assign = require('object-assign');
@@ -21,7 +22,7 @@ var MiddlePanelStore = assign({}, EventEmitter.prototype, {
var id = null;
var filter = this._datas.filter;
if (this._datas.filter_type) {
- key = this._datas.filter_type + '_id';
+ key = this._datas.filter_type;
id = this._datas.filter_id;
}
return this._datas.articles.filter(function(article) {
@@ -72,7 +73,7 @@ MiddlePanelStore.dispatchToken = JarrDispatcher.register(function(action) {
MiddlePanelStore.setArticles(action.articles);
MiddlePanelStore.emitChange();
break;
- case MiddlePanelActionTypes.MIDDLE_PANEL_PARENT_FILTER:
+ case MiddlePanelActionTypes.PARENT_FILTER:
changed = MiddlePanelStore.setParentFilter(action.filter_type,
action.filter_id);
changed = MiddlePanelStore.setArticles(action.articles) || changed;
@@ -83,6 +84,20 @@ MiddlePanelStore.dispatchToken = JarrDispatcher.register(function(action) {
changed = MiddlePanelStore.setArticles(action.articles) || changed;
if(changed) {MiddlePanelStore.emitChange();}
break;
+ case MiddlePanelActionTypes.CHANGE_ATTR:
+ var id = action.article_id;
+ var attr = action.attribute;
+ var val = action.value;
+ for (var i in MiddlePanelStore._datas.articles) {
+ if(MiddlePanelStore._datas.articles[i].article_id == id) {
+ if (MiddlePanelStore._datas.articles[i][attr] != val) {
+ MiddlePanelStore._datas.articles[i][attr] = val;
+ MiddlePanelStore.emitChange();
+ }
+ break;
+ }
+ }
+ break;
default:
// pass
}
diff --git a/src/web/views/views.py b/src/web/views/views.py
index 25788c42..297cae28 100644
--- a/src/web/views/views.py
+++ b/src/web/views/views.py
@@ -273,7 +273,7 @@ def get_middle_panel():
filters['like'] = True
filter_type = request.args.get('filter_type')
if filter_type in {'feed', 'category'} and request.args.get('filter_id'):
- filters[filter_type + '_id'] = int(request.args['filter_id'])
+ filters[filter_type + '_id'] = int(request.args['filter_id']) or None
fd_hash = {feed.id: {'title': feed.title,
'icon_url': url_for('icon.icon', url=feed.icon_url)
bgstack15