aboutsummaryrefslogtreecommitdiff
path: root/src/web/js
diff options
context:
space:
mode:
Diffstat (limited to 'src/web/js')
-rw-r--r--src/web/js/actions/MenuActions.js40
-rw-r--r--src/web/js/actions/MiddlePanelActions.js132
-rw-r--r--src/web/js/actions/RightPanelActions.js42
-rw-r--r--src/web/js/app.js18
-rw-r--r--src/web/js/components/MainApp.react.js29
-rw-r--r--src/web/js/components/Menu.react.js305
-rw-r--r--src/web/js/components/MiddlePanel.react.js267
-rw-r--r--src/web/js/components/Navbar.react.js138
-rw-r--r--src/web/js/components/RightPanel.react.js463
-rw-r--r--src/web/js/components/time.react.js15
-rw-r--r--src/web/js/constants/JarrConstants.js13
-rw-r--r--src/web/js/dispatcher/JarrDispatcher.js16
-rw-r--r--src/web/js/dispatcher/__tests__/AppDispatcher-test.js72
-rw-r--r--src/web/js/stores/MenuStore.js135
-rw-r--r--src/web/js/stores/MiddlePanelStore.js126
-rw-r--r--src/web/js/stores/RightPanelStore.js77
-rw-r--r--src/web/js/stores/__tests__/TodoStore-test.js90
17 files changed, 0 insertions, 1978 deletions
diff --git a/src/web/js/actions/MenuActions.js b/src/web/js/actions/MenuActions.js
deleted file mode 100644
index 824610d8..00000000
--- a/src/web/js/actions/MenuActions.js
+++ /dev/null
@@ -1,40 +0,0 @@
-var JarrDispatcher = require('../dispatcher/JarrDispatcher');
-var ActionTypes = require('../constants/JarrConstants');
-var jquery = require('jquery');
-
-
-var MenuActions = {
- // PARENT FILTERS
- reload: function(setFilterFunc, id) {
- jquery.getJSON('/menu', function(payload) {
- 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,
- });
- if(setFilterFunc && id) {
- setFilterFunc(id);
- }
- });
- },
- setFilter: function(filter) {
- JarrDispatcher.dispatch({
- type: ActionTypes.MENU_FILTER,
- filter: filter,
- });
- },
- toggleAllFolding: function(all_folded) {
- JarrDispatcher.dispatch({
- type: ActionTypes.TOGGLE_MENU_FOLD,
- all_folded: all_folded,
- });
- },
-};
-
-module.exports = MenuActions;
diff --git a/src/web/js/actions/MiddlePanelActions.js b/src/web/js/actions/MiddlePanelActions.js
deleted file mode 100644
index 700814d4..00000000
--- a/src/web/js/actions/MiddlePanelActions.js
+++ /dev/null
@@ -1,132 +0,0 @@
-var JarrDispatcher = require('../dispatcher/JarrDispatcher');
-var ActionTypes = require('../constants/JarrConstants');
-var jquery = require('jquery');
-var MiddlePanelStore = require('../stores/MiddlePanelStore');
-
-var _last_fetched_with = {};
-
-var reloadAndDispatch = function(dispath_payload) {
- var filters = MiddlePanelStore.getRequestFilter(
- dispath_payload.display_search);
- MiddlePanelStore.filter_whitelist.map(function(key) {
- if(key in dispath_payload) {
- filters[key] = dispath_payload[key];
- }
- if(filters[key] == null) {
- delete filters[key];
- }
- });
- if('display_search' in filters) {
- delete filters['display_search'];
- }
- jquery.getJSON('/middle_panel', filters,
- function(payload) {
- dispath_payload.articles = payload.articles;
- dispath_payload.filters = filters;
- JarrDispatcher.dispatch(dispath_payload);
- _last_fetched_with = MiddlePanelStore.getRequestFilter();
- });
-}
-
-
-var MiddlePanelActions = {
- reload: function() {
- reloadAndDispatch({
- type: ActionTypes.RELOAD_MIDDLE_PANEL,
- });
- },
- search: function(search) {
- reloadAndDispatch({
- type: ActionTypes.RELOAD_MIDDLE_PANEL,
- display_search: true,
- query: search.query,
- search_title: search.title,
- search_content: search.content,
- });
- },
- search_off: function() {
- reloadAndDispatch({
- type: ActionTypes.RELOAD_MIDDLE_PANEL,
- display_search: false,
- });
- },
- removeParentFilter: function() {
- reloadAndDispatch({
- type: ActionTypes.PARENT_FILTER,
- filter_type: null,
- filter_id: null,
- });
- },
- setCategoryFilter: function(category_id) {
- reloadAndDispatch({
- type: ActionTypes.PARENT_FILTER,
- filter_type: 'category_id',
- filter_id: category_id,
- });
- },
- setFeedFilter: function(feed_id) {
- reloadAndDispatch({
- type: ActionTypes.PARENT_FILTER,
- filter_type: 'feed_id',
- filter_id: feed_id,
- });
- },
- setFilter: function(filter) {
- reloadAndDispatch({
- type: ActionTypes.MIDDLE_PANEL_FILTER,
- filter: filter,
- });
- },
- changeRead: function(category_id, feed_id, 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 () {
- JarrDispatcher.dispatch({
- type: ActionTypes.CHANGE_ATTR,
- attribute: 'read',
- value_bool: new_value,
- value_num: new_value ? -1 : 1,
- articles: [{article_id: article_id,
- category_id: category_id,
- feed_id: feed_id}],
- });
- },
- });
- },
- changeLike: function(category_id, feed_id, 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 () {
- JarrDispatcher.dispatch({
- type: ActionTypes.CHANGE_ATTR,
- attribute: 'liked',
- value_bool: new_value,
- value_num: new_value ? -1 : 1,
- articles: [{article_id: article_id,
- category_id: category_id,
- feed_id: feed_id}],
- });
- },
- });
- },
- markAllAsRead: function() {
- var filters = MiddlePanelStore.getRequestFilter();
- jquery.ajax({type: 'PUT',
- contentType: 'application/json',
- data: JSON.stringify(filters),
- url: "/mark_all_as_read",
- success: function (payload) {
- JarrDispatcher.dispatch({
- type: ActionTypes.MARK_ALL_AS_READ,
- articles: payload.articles,
- });
- },
- });
- },
-};
-
-module.exports = MiddlePanelActions;
diff --git a/src/web/js/actions/RightPanelActions.js b/src/web/js/actions/RightPanelActions.js
deleted file mode 100644
index 5d78e001..00000000
--- a/src/web/js/actions/RightPanelActions.js
+++ /dev/null
@@ -1,42 +0,0 @@
-var jquery = require('jquery');
-var JarrDispatcher = require('../dispatcher/JarrDispatcher');
-var ActionTypes = require('../constants/JarrConstants');
-var MenuActions = require('../actions/MenuActions');
-
-var RightPanelActions = {
- loadArticle: function(article_id, was_read_before, to_parse) {
- var suffix = '';
- if(to_parse) {
- suffix = '/parse';
- }
- jquery.getJSON('/getart/' + article_id + suffix,
- function(payload) {
- JarrDispatcher.dispatch({
- type: ActionTypes.LOAD_ARTICLE,
- article: payload,
- was_read_before: was_read_before,
- });
- }
- );
- },
- _apiReq: function(meth, id, obj_type, data, success_callback) {
- var args = {type: meth, contentType: 'application/json',
- url: "api/v2.0/" + obj_type + "/" + id}
- if(data) {args.data = JSON.stringify(data);}
- if(success_callback) {args.success = success_callback;}
- jquery.ajax(args);
- },
- putObj: function(id, obj_type, fields) {
- this._apiReq('PUT', id, obj_type, fields, MenuActions.reload);
- },
- delObj: function(id, obj_type, fields) {
- this._apiReq('DELETE', id, obj_type, null, MenuActions.reload);
- },
- resetErrors: function(feed_id) {
- this._apiReq('PUT', feed_id, 'feed', {error_count: 0, last_error: ''},
- MenuActions.reload);
-
- },
-};
-
-module.exports = RightPanelActions;
diff --git a/src/web/js/app.js b/src/web/js/app.js
deleted file mode 100644
index 7837e6ae..00000000
--- a/src/web/js/app.js
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
- * Copyright (c) 2014, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- */
-
-var React = require('react');
-var ReactDOM = require('react-dom');
-
-var MainApp = require('./components/MainApp.react');
-
-ReactDOM.render(
- <MainApp />,
- document.getElementById('newspipeapp')
-);
diff --git a/src/web/js/components/MainApp.react.js b/src/web/js/components/MainApp.react.js
deleted file mode 100644
index 32bb663e..00000000
--- a/src/web/js/components/MainApp.react.js
+++ /dev/null
@@ -1,29 +0,0 @@
-var React = require('react');
-var createReactClass = require('create-react-class');
-var Col = require('react-bootstrap/lib/Col');
-var Grid = require('react-bootstrap/lib/Grid');
-var PropTypes = require('prop-types');
-
-var Menu = require('./Menu.react');
-var MiddlePanel = require('./MiddlePanel.react');
-var RightPanel = require('./RightPanel.react');
-
-
-var MainApp = createReactClass({
- render: function() {
- return (<div>
- <Grid fluid id="newspipe-container">
- <Menu />
- <Col id="middle-panel" mdOffset={3} lgOffset={2}
- xs={12} sm={4} md={4} lg={4}>
- <MiddlePanel.MiddlePanelFilter />
- <MiddlePanel.MiddlePanel />
- </Col>
- <RightPanel />
- </Grid>
- </div>
- );
- },
-});
-
-module.exports = MainApp;
diff --git a/src/web/js/components/Menu.react.js b/src/web/js/components/Menu.react.js
deleted file mode 100644
index 64672240..00000000
--- a/src/web/js/components/Menu.react.js
+++ /dev/null
@@ -1,305 +0,0 @@
-var React = require('react');
-var createReactClass = require('create-react-class');
-var Col = require('react-bootstrap/lib/Col');
-var Badge = require('react-bootstrap/lib/Badge');
-var Button = require('react-bootstrap/lib/Button');
-var ButtonGroup = require('react-bootstrap/lib/ButtonGroup');
-var Glyphicon = require('react-bootstrap/lib/Glyphicon');
-var PropTypes = require('prop-types');
-
-var MenuStore = require('../stores/MenuStore');
-var MenuActions = require('../actions/MenuActions');
-var MiddlePanelActions = require('../actions/MiddlePanelActions');
-
-var FeedItem = createReactClass({
- propTypes: {feed_id: PropTypes.number.isRequired,
- title: PropTypes.string.isRequired,
- unread: PropTypes.number.isRequired,
- error_count: PropTypes.number.isRequired,
- icon_url: PropTypes.string,
- active: PropTypes.bool.isRequired,
- },
- render: function() {
- var icon = null;
- var badge_unread = null;
- if(this.props.icon_url){
- icon = (<img width="16px" src={this.props.icon_url} />);
- } else {
- icon = <Glyphicon glyph="ban-circle" />;
- }
- if(this.props.unread){
- badge_unread = <Badge pullRight>{this.props.unread}</Badge>;
- }
- var classes = "nav-feed";
- if(this.props.active) {
- classes += " bg-primary";
- }
- if(this.props.error_count >= MenuStore._datas.max_error) {
- classes += " bg-danger";
- } else if(this.props.error_count > MenuStore._datas.error_threshold) {
- classes += " bg-warning";
- }
- var title = <span className="title">{this.props.title}</span>;
- return (<li className={classes} onClick={this.handleClick}>
- {icon}{title}{badge_unread}
- </li>
- );
- },
- handleClick: function() {
- MiddlePanelActions.setFeedFilter(this.props.feed_id);
- },
-});
-
-var Category = createReactClass({
- propTypes: {category_id: PropTypes.number,
- active_type: PropTypes.string,
- active_id: PropTypes.number},
- render: function() {
- var classes = "nav-cat";
- if((this.props.active_type == 'category_id'
- || this.props.category_id == null)
- && this.props.active_id == this.props.category_id) {
- classes += " bg-primary";
- }
- return (<li className={classes} onClick={this.handleClick}>
- {this.props.children}
- </li>
- );
- },
- handleClick: function(evnt) {
- // hack to avoid selection when clicking on folding icon
- if(!evnt.target.classList.contains('glyphicon')) {
- if(this.props.category_id != null) {
- MiddlePanelActions.setCategoryFilter(this.props.category_id);
- } else {
- MiddlePanelActions.removeParentFilter();
- }
- }
- },
-});
-
-var CategoryGroup = createReactClass({
- propTypes: {cat_id: PropTypes.number.isRequired,
- filter: PropTypes.string.isRequired,
- active_type: PropTypes.string,
- active_id: PropTypes.number,
- name: PropTypes.string.isRequired,
- feeds: PropTypes.array.isRequired,
- unread: PropTypes.number.isRequired,
- folded: PropTypes.bool,
- },
- getInitialState: function() {
- return {folded: false};
- },
- componentWillReceiveProps: function(nextProps) {
- if(nextProps.folded != null) {
- this.setState({folded: nextProps.folded});
- }
- },
- render: function() {
- // hidden the no category if empty
- if(!this.props.cat_id && !this.props.feeds.length) {
- return <ul className="hidden" />;
- }
- var filter = this.props.filter;
- var a_type = this.props.active_type;
- var a_id = this.props.active_id;
- if(!this.state.folded) {
- // filtering according to this.props.filter
- var feeds = this.props.feeds.filter(function(feed) {
- if (filter == 'unread' && feed.unread <= 0) {
- return false;
- } else if (filter == 'error' && feed.error_count <= MenuStore._datas.error_threshold) {
- return false;
- }
- return true;
- }).sort(function(feed_a, feed_b){
- return feed_b.unread - feed_a.unread;
- }).map(function(feed) {
- return (<FeedItem key={"f" + feed.id} feed_id={feed.id}
- title={feed.title} unread={feed.unread}
- error_count={feed.error_count}
- active={a_type == 'feed_id' && a_id == feed.id}
- icon_url={feed.icon_url} />
- );
- });
- } else {
- var feeds = [];
- }
- var unread = null;
- if(this.props.unread) {
- unread = <Badge pullRight>{this.props.unread}</Badge>;
- }
- var ctrl = (<Glyphicon onClick={this.toggleFolding}
- glyph={this.state.folded?"menu-right":"menu-down"} />
- );
-
- return (<ul className="nav nav-sidebar">
- <Category category_id={this.props.cat_id}
- active_id={this.props.active_id}
- active_type={this.props.active_type}>
- {ctrl}<h4>{this.props.name}</h4>{unread}
- </Category>
- {feeds}
- </ul>
- );
- },
- toggleFolding: function(evnt) {
- this.setState({folded: !this.state.folded});
- evnt.stopPropagation();
- },
-});
-
-var MenuFilter = createReactClass({
- propTypes: {feed_in_error: PropTypes.bool,
- filter: PropTypes.string.isRequired},
- getInitialState: function() {
- return {allFolded: false};
- },
- render: function() {
- var error_button = null;
- if (this.props.feed_in_error) {
- error_button = (
- <Button active={this.props.filter == "error"}
- title="Some of your feeds are in error, click here to list them"
- onClick={this.setErrorFilter}
- bsSize="small" bsStyle="warning">
- <Glyphicon glyph="exclamation-sign" />
- </Button>
- );
- }
- if(this.state.allFolded) {
- var foldBtnTitle = "Unfold all categories";
- var foldBtnGlyph = "option-horizontal";
- } else {
- var foldBtnTitle = "Fold all categories";
- var foldBtnGlyph = "option-vertical";
- }
- return (<div>
- <ButtonGroup className="nav nav-sidebar">
- <Button active={this.props.filter == "all"}
- title="Display all feeds"
- onClick={this.setAllFilter} bsSize="small">
- <Glyphicon glyph="menu-hamburger" />
- </Button>
- <Button active={this.props.filter == "unread"}
- title="Display only feed with unread article"
- onClick={this.setUnreadFilter}
- bsSize="small">
- <Glyphicon glyph="unchecked" />
- </Button>
- {error_button}
- </ButtonGroup>
- <ButtonGroup className="nav nav-sidebar">
- <Button onClick={MenuActions.reload}
- title="Refresh all feeds" bsSize="small">
- <Glyphicon glyph="refresh" />
- </Button>
- </ButtonGroup>
- <ButtonGroup className="nav nav-sidebar">
- <Button title={foldBtnTitle} bsSize="small"
- onClick={this.toggleFold}>
- <Glyphicon glyph={foldBtnGlyph} />
- </Button>
- </ButtonGroup>
- </div>
- );
- },
- setAllFilter: function() {
- MenuActions.setFilter("all");
- },
- setUnreadFilter: function() {
- MenuActions.setFilter("unread");
- },
- setErrorFilter: function() {
- MenuActions.setFilter("error");
- },
- toggleFold: function() {
- this.setState({allFolded: !this.state.allFolded}, function() {
- MenuActions.toggleAllFolding(this.state.allFolded);
- }.bind(this));
- },
-});
-
-var Menu = createReactClass({
- getInitialState: function() {
- return {filter: 'unread', categories: {}, feeds: {},
- all_folded: false, active_type: null, active_id: null};
- },
- render: function() {
- var feed_in_error = false;
- var rmPrntFilt = (
- <ul className="nav nav-sidebar">
- <Category category_id={null}
- active_id={this.state.active_id}
- active_type={this.state.active_type}>
- <h4>All</h4>
- </Category>
- </ul>
- );
- var categories = [];
- for(var index in this.state.categories_order) {
- var cat_id = this.state.categories_order[index];
- var feeds = [];
- var unread = 0;
- this.state.categories[cat_id].feeds.map(function(feed_id) {
- if(this.state.feeds[feed_id].error_count > MenuStore._datas.error_threshold) {
- feed_in_error = true;
- }
- unread += this.state.feeds[feed_id].unread;
- feeds.push(this.state.feeds[feed_id]);
- }.bind(this));
- categories.push(<CategoryGroup key={"c" + cat_id} feeds={feeds}
- filter={this.state.filter}
- active_type={this.state.active_type}
- active_id={this.state.active_id}
- name={this.state.categories[cat_id].name}
- cat_id={this.state.categories[cat_id].id}
- folded={this.state.all_folded}
- unread={unread} />);
- }
-
- return (<Col xsHidden smHidden md={3} lg={2}
- id="menu" className="sidebar">
- <MenuFilter filter={this.state.filter}
- feed_in_error={feed_in_error} />
- {rmPrntFilt}
- {categories}
- </Col>
- );
- },
- componentDidMount: function() {
- var setFilterFunc = null;
- var id = null;
- if(window.location.search.substring(1)) {
- var args = window.location.search.substring(1).split('&');
- args.map(function(arg) {
- if (arg.split('=')[0] == 'at' && arg.split('=')[1] == 'c') {
- setFilterFunc = MiddlePanelActions.setCategoryFilter;
- } else if (arg.split('=')[0] == 'at' && arg.split('=')[1] == 'f') {
- setFilterFunc = MiddlePanelActions.setFeedFilter;
-
- } else if (arg.split('=')[0] == 'ai') {
- id = parseInt(arg.split('=')[1]);
- }
- });
- }
- MenuActions.reload(setFilterFunc, id);
- MenuStore.addChangeListener(this._onChange);
- },
- componentWillUnmount: function() {
- MenuStore.removeChangeListener(this._onChange);
- },
- _onChange: function() {
- var datas = MenuStore.getAll();
- this.setState({filter: datas.filter,
- feeds: datas.feeds,
- categories: datas.categories,
- categories_order: datas.categories_order,
- active_type: datas.active_type,
- active_id: datas.active_id,
- all_folded: datas.all_folded});
- },
-});
-
-module.exports = Menu;
diff --git a/src/web/js/components/MiddlePanel.react.js b/src/web/js/components/MiddlePanel.react.js
deleted file mode 100644
index fc7c763a..00000000
--- a/src/web/js/components/MiddlePanel.react.js
+++ /dev/null
@@ -1,267 +0,0 @@
-var React = require('react');
-var createReactClass = require('create-react-class');
-
-var Row = require('react-bootstrap/lib/Row');
-var Button = require('react-bootstrap/lib/Button');
-var ButtonGroup = require('react-bootstrap/lib/ButtonGroup');
-var Glyphicon = require('react-bootstrap/lib/Glyphicon');
-var PropTypes = require('prop-types');
-
-var MiddlePanelStore = require('../stores/MiddlePanelStore');
-var MiddlePanelActions = require('../actions/MiddlePanelActions');
-var RightPanelActions = require('../actions/RightPanelActions');
-
-var JarrTime = require('./time.react');
-
-var TableLine = createReactClass({
- propTypes: {article_id: PropTypes.number.isRequired,
- feed_title: PropTypes.string.isRequired,
- icon_url: PropTypes.string,
- title: PropTypes.string.isRequired,
- rel_date: PropTypes.string.isRequired,
- date: PropTypes.string.isRequired,
- read: PropTypes.bool.isRequired,
- selected: PropTypes.bool.isRequired,
- liked: PropTypes.bool.isRequired,
- },
- getInitialState: function() {
- return {read: this.props.read, liked: this.props.liked,
- selected: false};
- },
- render: function() {
- 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 = <Glyphicon glyph="ban-circle" />;
- }
- var title = (<a href={'/article/redirect/' + this.props.article_id}
- onClick={this.openRedirectLink} target="_blank"
- title={this.props.feed_title}>
- {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} />);
- icon = <Glyphicon glyph={"new-window"} />;
- var clsses = "list-group-item";
- if(this.props.selected) {
- clsses += " active";
- }
- return (<div className={clsses} onClick={this.loadArticle} title={this.props.title}>
- <h5><strong>{title}</strong></h5>
- <JarrTime text={this.props.date}
- stamp={this.props.rel_date} />
- <div>{read} {liked} {this.props.title}</div>
- </div>
- );
- },
- openRedirectLink: function(evnt) {
- if(!this.state.read) {
- this.toogleRead(evnt);
- }
- },
- toogleRead: function(evnt) {
- this.setState({read: !this.state.read}, function() {
- MiddlePanelActions.changeRead(this.props.category_id,
- this.props.feed_id, this.props.article_id, this.state.read);
- }.bind(this));
- evnt.stopPropagation();
- },
- toogleLike: function(evnt) {
- this.setState({liked: !this.state.liked}, function() {
- MiddlePanelActions.changeLike(this.props.category_id,
- this.props.feed_id, this.props.article_id, this.state.liked);
- }.bind(this));
- evnt.stopPropagation();
- },
- loadArticle: function() {
- this.setState({selected: true, read: true}, function() {
- RightPanelActions.loadArticle(
- this.props.article_id, this.props.read);
- }.bind(this));
- },
- stopPropagation: function(evnt) {
- evnt.stopPropagation();
- },
-});
-
-var MiddlePanelSearchRow = createReactClass({
- getInitialState: function() {
- return {query: MiddlePanelStore._datas.query,
- search_title: MiddlePanelStore._datas.search_title,
- search_content: MiddlePanelStore._datas.search_content,
- };
- },
- render: function() {
- return (<Row>
- <form onSubmit={this.launchSearch}>
- <div className="input-group input-group-sm">
- <span className="input-group-addon">
- <span onClick={this.toogleSTitle}>Title</span>
- <input id="search-title" type="checkbox"
- onChange={this.toogleSTitle}
- checked={this.state.search_title}
- aria-label="Search title" />
- </span>
- <span className="input-group-addon">
- <span onClick={this.toogleSContent}>Content</span>
- <input id="search-content" type="checkbox"
- onChange={this.toogleSContent}
- checked={this.state.search_content}
- aria-label="Search content" />
- </span>
- <input type="text" className="form-control"
- onChange={this.setQuery}
- placeholder="Search text" />
- </div>
- </form>
- </Row>
- );
- },
- setQuery: function(evnt) {
- this.setState({query: evnt.target.value});
- },
- toogleSTitle: function() {
- this.setState({search_title: !this.state.search_title},
- this.launchSearch);
- },
- toogleSContent: function() {
- this.setState({search_content: !this.state.search_content},
- this.launchSearch);
- },
- launchSearch: function(evnt) {
- if(this.state.query && (this.state.search_title
- || this.state.search_content)) {
- MiddlePanelActions.search({query: this.state.query,
- title: this.state.search_title,
- content: this.state.search_content});
- }
- if(evnt) {
- evnt.preventDefault();
- }
- },
-});
-
-var MiddlePanelFilter = createReactClass({
- getInitialState: function() {
- return {filter: MiddlePanelStore._datas.filter,
- display_search: MiddlePanelStore._datas.display_search};
- },
- render: function() {
- var search_row = null;
- if(this.state.display_search) {
- search_row = <MiddlePanelSearchRow />
- }
- return (<div>
- <Row className="show-grid">
- <ButtonGroup>
- <Button active={this.state.filter == "all"}
- title="Display all articles"
- onClick={this.setAllFilter} bsSize="small">
- <Glyphicon glyph="menu-hamburger" />
- </Button>
- <Button active={this.state.filter == "unread"}
- title="Display only unread article"
- onClick={this.setUnreadFilter}
- bsSize="small">
- <Glyphicon glyph="unchecked" />
- </Button>
- <Button active={this.state.filter == "liked"}
- title="Filter only liked articles"
- onClick={this.setLikedFilter}
- bsSize="small">
- <Glyphicon glyph="star" />
- </Button>
- </ButtonGroup>
- <ButtonGroup>
- <Button onClick={this.toogleSearch}
- title="Search through displayed articles"
- bsSize="small">
- <Glyphicon glyph="search" />
- </Button>
- </ButtonGroup>
- <ButtonGroup>
- <Button onClick={MiddlePanelActions.markAllAsRead}
- title="Mark all displayed article as read"
- bsSize="small">
- <Glyphicon glyph="trash" />
- </Button>
- </ButtonGroup>
- </Row>
- {search_row}
- </div>
- );
- },
- setAllFilter: function() {
- this.setState({filter: 'all'}, function() {
- MiddlePanelActions.setFilter('all');
- }.bind(this));
- },
- setUnreadFilter: function() {
- this.setState({filter: 'unread'}, function() {
- MiddlePanelActions.setFilter('unread');
- }.bind(this));
- },
- setLikedFilter: function() {
- this.setState({filter: 'liked'}, function() {
- MiddlePanelActions.setFilter('liked');
- }.bind(this));
- },
- toogleSearch: function() {
- this.setState({display_search: !this.state.display_search},
- function() {
- if(!this.state.display_search) {
- MiddlePanelActions.search_off();
- }
- }.bind(this)
- );
- },
-});
-
-var MiddlePanel = createReactClass({
- getInitialState: function() {
- return {filter: MiddlePanelStore._datas.filter, articles: []};
- },
- render: function() {
- return (<Row className="show-grid">
- <div className="list-group">
- {this.state.articles.map(function(article){
- var key = "a" + article.article_id;
- if(article.read) {key+="r";}
- if(article.liked) {key+="l";}
- if(article.selected) {key+="s";}
- return (<TableLine key={key}
- title={article.title}
- icon_url={article.icon_url}
- read={article.read}
- liked={article.liked}
- rel_date={article.rel_date}
- date={article.date}
- selected={article.selected}
- article_id={article.article_id}
- feed_id={article.feed_id}
- locales={['en']}
- category_id={article.category_id}
- feed_title={article.feed_title} />);})}
- </div>
- </Row>
- );
- },
- componentDidMount: function() {
- MiddlePanelActions.reload();
- MiddlePanelStore.addChangeListener(this._onChange);
- },
- componentWillUnmount: function() {
- MiddlePanelStore.removeChangeListener(this._onChange);
- },
- _onChange: function() {
- this.setState({filter: MiddlePanelStore._datas.filter,
- articles: MiddlePanelStore.getArticles()});
- },
-});
-
-module.exports = {MiddlePanel: MiddlePanel,
- MiddlePanelFilter: MiddlePanelFilter};
diff --git a/src/web/js/components/Navbar.react.js b/src/web/js/components/Navbar.react.js
deleted file mode 100644
index 83f3c72c..00000000
--- a/src/web/js/components/Navbar.react.js
+++ /dev/null
@@ -1,138 +0,0 @@
-var React = require('react');
-var createReactClass = require('create-react-class');
-var Glyphicon = require('react-bootstrap/lib/Glyphicon');
-var Nav = require('react-bootstrap/lib/Nav');
-var NavItem = require('react-bootstrap/lib/NavItem');
-var Navbar = require('react-bootstrap/lib/Navbar');
-var NavDropdown = require('react-bootstrap/lib/NavDropdown');
-var MenuItem = require('react-bootstrap/lib/MenuItem');
-var Modal = require('react-bootstrap/lib/Modal');
-var Button = require('react-bootstrap/lib/Button');
-var Input = require('react-bootstrap/lib/Input');
-
-var MenuStore = require('../stores/MenuStore');
-
-JarrNavBar = createReactClass({
- getInitialState: function() {
- return {is_admin: MenuStore._datas.is_admin,
- crawling_method: MenuStore._datas.crawling_method,
- showModal: false, modalType: null};
- },
- buttonFetch: function() {
- if(this.state.is_admin && this.state.crawling_method != 'http') {
- return (<NavItem eventKey={2} href="/fetch">
- <Glyphicon glyph="import" />Fetch
- </NavItem>);
- }
- },
- sectionAdmin: function() {
- if(this.state.is_admin) {
- return (<MenuItem href="/admin/dashboard">
- <Glyphicon glyph="dashboard" />Dashboard
- </MenuItem>);
- }
- },
- getModal: function() {
- var heading = null;
- var action = null;
- var body = null;
- if(this.state.modalType == 'addFeed') {
- heading = 'Add a new feed';
- action = '/feed/bookmarklet';
- placeholder = "Site or feed url";
- body = <Input name="url" type="text" placeholder={placeholder} />;
- } else {
- heading = 'Add a new category';
- action = '/category/create';
- body = <Input name="name" type="text"
- placeholder="Name" />;
- }
- return (<Modal show={this.state.showModal} onHide={this.close}>
- <form action={action} method="POST">
- <Modal.Header closeButton>
- <Modal.Title>{heading}</Modal.Title>
- </Modal.Header>
- <Modal.Body>
- {body}
- </Modal.Body>
- <Modal.Footer>
- <Button type="submit">Add</Button>
- </Modal.Footer>
- </form>
- </Modal>);
- },
- close: function() {
- this.setState({showModal: false, modalType: null});
- },
- openAddFeed: function() {
- this.setState({showModal: true, modalType: 'addFeed'});
- },
- openAddCategory: function() {
- this.setState({showModal: true, modalType: 'addCategory'});
- },
- render: function() {
- return (<Navbar fixedTop inverse id="newspipenav" fluid staticTop={true}>
- {this.getModal()}
- <Navbar.Header>
- <Navbar.Brand>
- <a href="/">Newspipe</a>
- </Navbar.Brand>
- <Navbar.Toggle />
- </Navbar.Header>
- <Navbar.Collapse>
- <Nav pullRight>
- {this.buttonFetch()}
- <NavItem className="newspipenavitem"
- onClick={this.openAddFeed} href="#">
- <Glyphicon glyph="plus-sign" />Add a new feed
- </NavItem>
- <NavItem className="newspipenavitem"
- onClick={this.openAddCategory} href="#">
- <Glyphicon glyph="plus-sign" />Add a new category
- </NavItem>
- <NavDropdown title="Feed" id="feed-dropdown">
- <MenuItem href="/feeds/inactives">
- Inactives
- </MenuItem>
- <MenuItem href="/articles/history">
- History
- </MenuItem>
- <MenuItem href="/feeds/">
- All
- </MenuItem>
- </NavDropdown>
- <NavDropdown title={<Glyphicon glyph='user' />}
- id="user-dropdown">
- <MenuItem href="/user/profile">
- <Glyphicon glyph="user" />Profile
- </MenuItem>
- <MenuItem href="/user/management">
- <Glyphicon glyph="cog" />Your data
- </MenuItem>
- <MenuItem href="/about">
- <Glyphicon glyph="question-sign" />About
- </MenuItem>
- {this.sectionAdmin()}
- <MenuItem href="/logout">
- <Glyphicon glyph="log-out" />Logout
- </MenuItem>
- </NavDropdown>
- </Nav>
- </Navbar.Collapse>
- </Navbar>
- );
- },
- componentDidMount: function() {
- MenuStore.addChangeListener(this._onChange);
- },
- componentWillUnmount: function() {
- MenuStore.removeChangeListener(this._onChange);
- },
- _onChange: function() {
- var datas = MenuStore.getAll();
- this.setState({is_admin: datas.is_admin,
- crawling_method: datas.crawling_method});
- },
-});
-
-module.exports = JarrNavBar;
diff --git a/src/web/js/components/RightPanel.react.js b/src/web/js/components/RightPanel.react.js
deleted file mode 100644
index 6384cdfe..00000000
--- a/src/web/js/components/RightPanel.react.js
+++ /dev/null
@@ -1,463 +0,0 @@
-var React = require('react');
-var createReactClass = require('create-react-class');
-var Col = require('react-bootstrap/lib/Col');
-var Glyphicon = require('react-bootstrap/lib/Glyphicon');
-var Button = require('react-bootstrap/lib/Button');
-var ButtonGroup = require('react-bootstrap/lib/ButtonGroup');
-var Modal = require('react-bootstrap/lib/Modal');
-var PropTypes = require('prop-types');
-
-var RightPanelActions = require('../actions/RightPanelActions');
-var RightPanelStore = require('../stores/RightPanelStore');
-var MenuStore = require('../stores/MenuStore');
-var JarrTime = require('./time.react');
-
-var PanelMixin = {
- propTypes: {obj: PropTypes.object.isRequired},
- getInitialState: function() {
- return {edit_mode: false, obj: this.props.obj, showModal: false};
- },
- getHeader: function() {
- var icon = null;
- if(this.props.obj.icon_url){
- icon = (<img width="16px" src={this.props.obj.icon_url} />);
- }
- var btn_grp = null;
- if(this.isEditable() || this.isRemovable()) {
- var edit_button = null;
- if(this.isEditable()) {
- edit_button = (<Button onClick={this.onClickEdit}>
- <Glyphicon glyph="pencil" />
- </Button>);
- }
- var rem_button = null;
- if(this.isRemovable()) {
- rem_button = (<Button onClick={this.onClickRemove}>
- <Glyphicon glyph="remove-sign" />
- </Button>);
- }
- btn_grp = (<ButtonGroup bsSize="small">
- {edit_button}
- {rem_button}
- </ButtonGroup>);
- }
- return (<div id="right-panel-heading" className="panel-heading">
- <Modal show={this.state.showModal} onHide={this.closeModal}>
- <Modal.Header closeButton>
- <Modal.Title>Are you sure ?</Modal.Title>
- </Modal.Header>
- <Modal.Footer>
- <Button onClick={this.confirmDelete}>
- Confirm
- </Button>
- </Modal.Footer>
- </Modal>
-
- <h4>{icon}{this.getTitle()}</h4>
- {btn_grp}
- </div>);
- },
- getKey: function(prefix, suffix) {
- return ((this.state.edit_mode?'edit':'fix') + prefix
- + '-' + this.props.obj.id + '-' + suffix);
- },
- getCore: function() {
- var items = [];
- var key;
- if(!this.state.edit_mode) {
- this.fields.filter(function(field) {
- return field.type != 'ignore';
- }).map(function(field) {
- key = this.getKey('dt', field.key);
- items.push(<dt key={key}>{field.title}</dt>);
- key = this.getKey('dd', field.key);
- if(field.type == 'string') {
- items.push(<dd key={key}>{this.props.obj[field.key]}</dd>);
- } else if(field.type == 'bool') {
- if(this.props.obj[field.key]) {
- items.push(<dd key={key}><Glyphicon glyph="check" /></dd>);
- } else {
- items.push(<dd key={key}><Glyphicon glyph="unchecked" /></dd>);
- }
- } else if (field.type == 'list') {
- items.push(<dd key={key}>{this.props.obj[field.key].reduce(function(previousTag, currentTag) {
- return currentTag.concat(", ", previousTag)
- }, "")}</dd>)
- } else if (field.type == 'link') {
- items.push(<dd key={key}>
- <a href={this.props.obj[field.key]}>
- {this.props.obj[field.key]}
- </a>
- </dd>);
- }
- }.bind(this));
- } else {
- this.fields.filter(function(field) {
- return field.type != 'ignore';
- }).map(function(field) {
- key = this.getKey('dd', field.key);
- items.push(<dt key={key}>{field.title}</dt>);
- key = this.getKey('dt', field.key);
- var input = null;
- if(field.type == 'string' || field.type == 'link') {
- input = (<input type="text" name={field.key}
- onChange={this.saveField}
- defaultValue={this.props.obj[field.key]} />);
- } else if (field.type == 'bool') {
- input = (<input type="checkbox" name={field.key}
- onChange={this.saveField}
- defaultChecked={this.props.obj[field.key]} />);
- }
- items.push(<dd key={key}>{input}</dd>);
- }.bind(this));
- }
- return (<dl className="dl-horizontal">{items}</dl>);
- },
- getSubmit: function() {
- return (<dd key={this.getKey('dd', 'submit')}>
- <button className="btn btn-default" onClick={this.saveObj}>
- Submit
- </button>
- </dd>);
- },
- render: function() {
- return (<div className="panel panel-default">
- {this.getHeader()}
- {this.getBody()}
- </div>
- );
- },
- onClickEdit: function() {
- this.setState({edit_mode: !this.state.edit_mode});
- },
- onClickRemove: function() {
- this.setState({showModal: true});
- },
- closeModal: function() {
- this.setState({showModal: false});
- },
- confirmDelete: function() {
- this.setState({showModal: false}, function() {
- RightPanelActions.delObj(this.props.obj.id, this.obj_type);
- }.bind(this));
- },
- saveField: function(evnt) {
- var obj = this.state.obj;
- for(var i in this.fields) {
- if(evnt.target.name == this.fields[i].key) {
- if(this.fields[i].type == 'bool') {
- obj[evnt.target.name] = evnt.target.checked;
- } else {
- obj[evnt.target.name] = evnt.target.value;
- }
- break;
- }
- }
- this.setState({obj: obj});
- },
- saveObj: function() {
- var to_push = {};
- this.fields.map(function(field) {
- to_push[field.key] = this.state.obj[field.key];
- }.bind(this));
- this.setState({edit_mode: false}, function() {
- RightPanelActions.putObj(this.props.obj.id, this.obj_type, to_push);
- }.bind(this));
- },
-};
-
-var Article = createReactClass({
- mixins: [PanelMixin],
- isEditable: function() {return false;},
- isRemovable: function() {return true;},
- fields: [{'title': 'Date', 'type': 'string', 'key': 'date'},
- {'title': 'Original link', 'type': 'link', 'key': 'link'},
- {'title': 'Tags', 'type': 'list', 'key': 'tags'}
- ],
- obj_type: 'article',
- getTitle: function() {return this.props.obj.title;},
- getBody: function() {
- return (<div className="panel-body">
- {this.getCore()}
- <div id="article-content" dangerouslySetInnerHTML={
- {__html: this.props.obj.content}} />
- </div>);
- }
-});
-
-var Feed = createReactClass({
- mixins: [PanelMixin],
- isEditable: function() {return true;},
- isRemovable: function() {return true;},
- obj_type: 'feed',
- fields: [{'title': 'Feed title', 'type': 'string', 'key': 'title'},
- {'title': 'Description', 'type': 'string', 'key': 'description'},
- {'title': 'Feed link', 'type': 'link', 'key': 'link'},
- {'title': 'Site link', 'type': 'link', 'key': 'site_link'},
- {'title': 'Enabled', 'type': 'bool', 'key': 'enabled'},
- {'title': 'Private', 'type': 'bool', 'key': 'private'},
- {'title': 'Filters', 'type': 'ignore', 'key': 'filters'},
- {'title': 'Category', 'type': 'ignore', 'key': 'category_id'},
- ],
- getTitle: function() {return this.props.obj.title;},
- getFilterRow: function(i, filter) {
- return (<dd key={'d' + i + '-' + this.props.obj.id}
- className="input-group filter-row">
- <span className="input-group-btn">
- <button className="btn btn-default" type="button"
- data-index={i} onClick={this.removeFilterRow}>
- <Glyphicon glyph='minus' />
- </button>
- </span>
- <select name="action on" className="form-control"
- data-index={i} onChange={this.saveFilterChange}
- defaultValue={filter['action on']}>
- <option value="match">match</option>
- <option value="no match">no match</option>
- </select>
- <input name="pattern" type="text" className="form-control"
- data-index={i} onChange={this.saveFilterChange}
- defaultValue={filter.pattern} />
- <select name="type" className="form-control"
- data-index={i} onChange={this.saveFilterChange}
- defaultValue={filter.type}>
- <option value='simple match'>simple match</option>
- <option value='regex'>regex</option>
- </select>
- <select name="action" className="form-control"
- data-index={i} onChange={this.saveFilterChange}
- defaultValue={filter.action}>
- <option value="mark as read">mark as read</option>
- <option value="mark as favorite">mark as favorite</option>
- </select>
- </dd>);
- },
- getFilterRows: function() {
- var rows = [];
- if(this.state.edit_mode) {
- for(var i in this.state.obj.filters) {
- rows.push(this.getFilterRow(i, this.state.obj.filters[i]));
- }
- return (<dl className="dl-horizontal">
- <dt>Filters</dt>
- <dd>
- <button className="btn btn-default"
- type="button" onClick={this.addFilterRow}>
- <Glyphicon glyph='plus' />
- </button>
- </dd>
- {rows}
- </dl>);
- }
- rows = [];
- rows.push(<dt key={'d-title'}>Filters</dt>);
- for(var i in this.state.obj.filters) {
- rows.push(<dd key={'d' + i}>
- When {this.state.obj.filters[i]['action on']}
- on "{this.state.obj.filters[i].pattern}"
- ({this.state.obj.filters[i].type})
- "=" {this.state.obj.filters[i].action}
- </dd>);
- }
- return <dl className="dl-horizontal">{rows}</dl>;
- },
- getCategorySelect: function() {
- var content = null;
- if(this.state.edit_mode) {
- var categ_options = [];
- for(var index in MenuStore._datas.categories_order) {
- var cat_id = MenuStore._datas.categories_order[index];
- categ_options.push(
- <option value={cat_id}
- key={MenuStore._datas.categories[cat_id].id}>
- {MenuStore._datas.categories[cat_id].name}
- </option>);
- }
- content = (<select name="category_id" className="form-control"
- onChange={this.saveField}
- defaultValue={this.props.obj.category_id}>
- {categ_options}
- </select>);
- } else {
- content = MenuStore._datas.categories[this.props.obj.category_id].name;
- }
- return (<dl className="dl-horizontal">
- <dt>Category</dt><dd>{content}</dd>
- </dl>);
- },
- getErrorFields: function() {
- if(this.props.obj.error_count < MenuStore._datas.error_threshold) {
- return;
- }
- if(this.props.obj.error_count < MenuStore._datas.max_error) {
- return (<dl className="dl-horizontal">
- <dt>State</dt>
- <dd>The download of this feed has encountered some problems. However its error counter will be reinitialized at the next successful retrieving.</dd>
- <dt>Last error</dt>
- <dd>{this.props.obj.last_error}</dd>
- </dl>);
- }
- return (<dl className="dl-horizontal">
- <dt>State</dt>
- <dd>That feed has encountered too much consecutive errors and won't be retrieved anymore.</dd>
-
- <dt>Last error</dt>
- <dd>{this.props.obj.last_error}</dd>
- <dd>
- <Button onClick={this.resetErrors}>Reset error count
- </Button>
- </dd>
- </dl>);
-
- },
- resetErrors: function() {
- var obj = this.state.obj;
- obj.error_count = 0;
- this.setState({obj: obj}, function() {
- RightPanelActions.resetErrors(this.props.obj.id);
- }.bind(this));
- },
- getBody: function() {
- return (<div className="panel-body">
- <dl className="dl-horizontal">
- <dt>Created on</dt>
- <dd><JarrTime stamp={this.props.obj.created_rel}
- text={this.props.obj.created_date} />
- </dd>
- <dt>Last fetched</dt>
- <dd><JarrTime stamp={this.props.obj.last_rel}
- text={this.props.obj.last_retrieved} />
- </dd>
- </dl>
- {this.getErrorFields()}
- {this.getCategorySelect()}
- {this.getCore()}
- {this.getFilterRows()}
- {this.state.edit_mode?this.getSubmit():null}
- </div>
- );
- },
- addFilterRow: function() {
- var obj = this.state.obj;
- obj.filters.push({action: "mark as read", 'action on': "match",
- type: "simple match", pattern: ""});
- this.setState({obj: obj});
- },
- removeFilterRow: function(evnt) {
- var obj = this.state.obj;
- delete obj.filters[evnt.target.getAttribute('data-index')];
- this.setState({obj: obj});
- },
- saveFilterChange: function(evnt) {
- var index = evnt.target.getAttribute('data-index');
- var obj = this.state.obj;
- obj.filters[index][evnt.target.name] = evnt.target.value;
- this.setState({obj: obj});
- },
-});
-
-var Category = createReactClass({
- mixins: [PanelMixin],
- isEditable: function() {
- if(this.props.obj.id != 0) {return true;}
- else {return false;}
- },
- isRemovable: function() {return this.isEditable();},
- obj_type: 'category',
- fields: [{'title': 'Category name', 'type': 'string', 'key': 'name'}],
- getTitle: function() {return this.props.obj.name;},
- getBody: function() {
- return (<div className="panel-body">
- {this.getCore()}
- {this.state.edit_mode?this.getSubmit():null}
- </div>);
- },
-});
-
-var RightPanel = createReactClass({
- getInitialState: function() {
- return {category: null, feed: null, article: null, current: null};
- },
- getCategoryCrum: function() {
- return (<li><a onClick={this.selectCategory} href="#">
- {this.state.category.name}
- </a></li>);
- },
- getFeedCrum: function() {
- return (<li><a onClick={this.selectFeed} href="#">
- {this.state.feed.title}
- </a></li>);
- },
- getArticleCrum: function() {
- return <li>{this.state.article.title}</li>;
- },
- render: function() {
- window.scrollTo(0, 0);
- var brd_category = null;
- var brd_feed = null;
- var brd_article = null;
- var breadcrum = null;
- if(this.state.category) {
- brd_category = (<li className="rp-crum">
- <a onClick={this.selectCategory} href="#">
- {this.state.category.name}
- </a>
- </li>);
- }
- if(this.state.feed) {
- brd_feed = (<li className="rp-crum">
- <a onClick={this.selectFeed} href="#">
- {this.state.feed.title}
- </a>
- </li>);
- }
- if(this.state.article) {
- brd_article = <li className="rp-crum">{this.state.article.title}</li>;
- }
- if(brd_category || brd_feed || brd_article) {
- breadcrum = (<ol className="breadcrumb" id="rp-breadcrum">
- {brd_category}
- {brd_feed}
- {brd_article}
- </ol>);
- }
- if(this.state.current == 'article') {
- var cntnt = (<Article type='article' obj={this.state.article}
- key={this.state.article.id} />);
- } else if(this.state.current == 'feed') {
- var cntnt = (<Feed type='feed' obj={this.state.feed}
- key={this.state.feed.id} />);
- } else if(this.state.current == 'category') {
- var cntnt = (<Category type='category' obj={this.state.category}
- key={this.state.category.id} />);
- }
-
- return (<Col id="right-panel" xsHidden
- smOffset={4} mdOffset={7} lgOffset={6}
- sm={8} md={5} lg={6}>
- {breadcrum}
- {cntnt}
- </Col>
- );
- },
- selectCategory: function() {
- this.setState({current: 'category'});
- },
- selectFeed: function() {
- this.setState({current: 'feed'});
- },
- selectArticle: function() {
- this.setState({current: 'article'});
- },
- componentDidMount: function() {
- RightPanelStore.addChangeListener(this._onChange);
- },
- componentWillUnmount: function() {
- RightPanelStore.removeChangeListener(this._onChange);
- },
- _onChange: function() {
- this.setState(RightPanelStore.getAll());
- },
-});
-
-module.exports = RightPanel;
diff --git a/src/web/js/components/time.react.js b/src/web/js/components/time.react.js
deleted file mode 100644
index 07e1fbdf..00000000
--- a/src/web/js/components/time.react.js
+++ /dev/null
@@ -1,15 +0,0 @@
-var React = require('react');
-var createReactClass = require('create-react-class');
-var PropTypes = require('prop-types');
-
-var JarrTime = createReactClass({
- propTypes: {stamp: PropTypes.string.isRequired,
- text: PropTypes.string.isRequired},
- render: function() {
- return (<time dateTime={this.props.text} title={this.props.text}>
- {this.props.stamp}
- </time>);
- },
-});
-
-module.exports = JarrTime;
diff --git a/src/web/js/constants/JarrConstants.js b/src/web/js/constants/JarrConstants.js
deleted file mode 100644
index 78e8bf04..00000000
--- a/src/web/js/constants/JarrConstants.js
+++ /dev/null
@@ -1,13 +0,0 @@
-var keyMirror = require('keymirror');
-
-module.exports = keyMirror({
- TOGGLE_MENU_FOLD: null,
- RELOAD_MENU: null,
- PARENT_FILTER: null, // set a feed or a category as filter in menu
- MENU_FILTER: null, // change displayed feed in the menu
- CHANGE_ATTR: null, // edit an attr on an article (like / read)
- RELOAD_MIDDLE_PANEL: null,
- MIDDLE_PANEL_FILTER: null, // set a filter (read/like/all)
- LOAD_ARTICLE: null, // load a single article in right panel
- MARK_ALL_AS_READ: null,
-});
diff --git a/src/web/js/dispatcher/JarrDispatcher.js b/src/web/js/dispatcher/JarrDispatcher.js
deleted file mode 100644
index 56da186f..00000000
--- a/src/web/js/dispatcher/JarrDispatcher.js
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright (c) 2014-2015, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- * AppDispatcher
- *
- * A singleton that operates as the central hub for application updates.
- */
-
-var Dispatcher = require('flux').Dispatcher;
-
-module.exports = new Dispatcher();
diff --git a/src/web/js/dispatcher/__tests__/AppDispatcher-test.js b/src/web/js/dispatcher/__tests__/AppDispatcher-test.js
deleted file mode 100644
index d3a35fc5..00000000
--- a/src/web/js/dispatcher/__tests__/AppDispatcher-test.js
+++ /dev/null
@@ -1,72 +0,0 @@
-"use strict";
-
-jest.autoMockOff();
-
-describe('AppDispatcher', function() {
- var AppDispatcher;
-
- beforeEach(function() {
- AppDispatcher = require('../AppDispatcher.js');
- });
-
- it('sends actions to subscribers', function() {
- var listener = jest.genMockFunction();
- AppDispatcher.register(listener);
-
- var payload = {};
- AppDispatcher.dispatch(payload);
- expect(listener.mock.calls.length).toBe(1);
- expect(listener.mock.calls[0][0]).toBe(payload);
- });
-
- it('waits with chained dependencies properly', function() {
- var payload = {};
-
- var listener1Done = false;
- var listener1 = function(pl) {
- AppDispatcher.waitFor([index2, index4]);
- // Second, third, and fourth listeners should have now been called
- expect(listener2Done).toBe(true);
- expect(listener3Done).toBe(true);
- expect(listener4Done).toBe(true);
- listener1Done = true;
- };
- var index1 = AppDispatcher.register(listener1);
-
- var listener2Done = false;
- var listener2 = function(pl) {
- AppDispatcher.waitFor([index3]);
- expect(listener3Done).toBe(true);
- listener2Done = true;
- };
- var index2 = AppDispatcher.register(listener2);
-
- var listener3Done = false;
- var listener3 = function(pl) {
- listener3Done = true;
- };
- var index3 = AppDispatcher.register(listener3);
-
- var listener4Done = false;
- var listener4 = function(pl) {
- AppDispatcher.waitFor([index3]);
- expect(listener3Done).toBe(true);
- listener4Done = true;
- };
- var index4 = AppDispatcher.register(listener4);
-
- runs(function() {
- AppDispatcher.dispatch(payload);
- });
-
- waitsFor(function() {
- return listener1Done;
- }, "Not all subscribers were properly called", 500);
-
- runs(function() {
- expect(listener1Done).toBe(true);
- expect(listener2Done).toBe(true);
- expect(listener3Done).toBe(true);
- });
- });
-});
diff --git a/src/web/js/stores/MenuStore.js b/src/web/js/stores/MenuStore.js
deleted file mode 100644
index 770bc501..00000000
--- a/src/web/js/stores/MenuStore.js
+++ /dev/null
@@ -1,135 +0,0 @@
-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 MenuStore = assign({}, EventEmitter.prototype, {
- _datas: {filter: 'unread', feeds: {}, categories: {}, categories_order: [],
- active_type: null, active_id: null,
- is_admin: false, crawling_method: 'default',
- all_unread_count: 0, max_error: 0, error_threshold: 0,
- all_folded: false},
- getAll: function() {
- return this._datas;
- },
- setFilter: function(value) {
- if(this._datas.filter != value) {
- this._datas.filter = value;
- this._datas.all_folded = null;
- 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._datas.all_folded = null;
- this.emitChange();
- }
- },
- emitChange: function() {
- this.emit(CHANGE_EVENT);
- },
- addChangeListener: function(callback) {
- this.on(CHANGE_EVENT, callback);
- },
- removeChangeListener: function(callback) {
- this.removeListener(CHANGE_EVENT, callback);
- },
-});
-
-
-MenuStore.dispatchToken = JarrDispatcher.register(function(action) {
- switch(action.type) {
- case ActionTypes.RELOAD_MENU:
- MenuStore._datas['feeds'] = action.feeds;
- MenuStore._datas['categories'] = action.categories;
- MenuStore._datas['categories_order'] = action.categories_order;
- MenuStore._datas['is_admin'] = action.is_admin;
- MenuStore._datas['max_error'] = action.max_error;
- MenuStore._datas['error_threshold'] = action.error_threshold;
- MenuStore._datas['crawling_method'] = action.crawling_method;
- MenuStore._datas['all_unread_count'] = action.all_unread_count;
- MenuStore._datas.all_folded = null;
- MenuStore.emitChange();
- break;
- case ActionTypes.PARENT_FILTER:
- MenuStore.setActive(action.filter_type, action.filter_id);
- if(action.filters && action.articles && !action.filters.query
- && action.filters.filter == 'unread') {
- var new_unread = {};
- action.articles.map(function(article) {
- if(!(article.feed_id in new_unread)) {
- new_unread[article.feed_id] = 0;
- }
- if(!article.read) {
- new_unread[article.feed_id] += 1;
- }
- });
- var changed = false;
- for(var feed_id in new_unread) {
- var old_unread = MenuStore._datas.feeds[feed_id].unread;
- if(old_unread == new_unread[feed_id]) {
- continue;
- }
- changed = true;
- MenuStore._datas.feeds[feed_id].unread = new_unread[feed_id];
- var cat_id = MenuStore._datas.feeds[feed_id].category_id;
- MenuStore._datas.categories[cat_id].unread -= old_unread;
- MenuStore._datas.categories[cat_id].unread += new_unread[feed_id];
- }
- if(changed) {
- MenuStore._datas.all_folded = null;
- MenuStore.emitChange();
- }
- }
- break;
- case ActionTypes.MENU_FILTER:
- MenuStore.setFilter(action.filter);
- break;
- case ActionTypes.CHANGE_ATTR:
- if(action.attribute != 'read') {
- return;
- }
- var val = action.value_num;
- action.articles.map(function(article) {
- MenuStore._datas.categories[article.category_id].unread += val;
- MenuStore._datas.feeds[article.feed_id].unread += val;
- });
- MenuStore._datas.all_folded = null;
- MenuStore.emitChange();
- break;
- case ActionTypes.LOAD_ARTICLE:
- if(!action.was_read_before) {
- MenuStore._datas.categories[action.article.category_id].unread -= 1;
- MenuStore._datas.feeds[action.article.feed_id].unread -= 1;
- MenuStore._datas.all_folded = null;
- MenuStore.emitChange();
- }
- break;
- case ActionTypes.TOGGLE_MENU_FOLD:
- MenuStore._datas.all_folded = action.all_folded;
- MenuStore.emitChange();
- break;
- case ActionTypes.MARK_ALL_AS_READ:
- action.articles.map(function(art) {
- if(!art.read) {
- MenuStore._datas.feeds[art.feed_id].unread -= 1;
- if(art.category_id) {
- MenuStore._datas.categories[art.category_id].unread -= 1;
-
- }
- }
- });
-
- MenuStore._datas.all_folded = null;
- MenuStore.emitChange();
- break;
- default:
- // do nothing
- }
-});
-
-module.exports = MenuStore;
diff --git a/src/web/js/stores/MiddlePanelStore.js b/src/web/js/stores/MiddlePanelStore.js
deleted file mode 100644
index c554f929..00000000
--- a/src/web/js/stores/MiddlePanelStore.js
+++ /dev/null
@@ -1,126 +0,0 @@
-var JarrDispatcher = require('../dispatcher/JarrDispatcher');
-var ActionTypes = require('../constants/JarrConstants');
-var EventEmitter = require('events').EventEmitter;
-var CHANGE_EVENT = 'change_middle_panel';
-var assign = require('object-assign');
-
-
-var MiddlePanelStore = assign({}, EventEmitter.prototype, {
- filter_whitelist: ['filter', 'filter_id', 'filter_type', 'display_search',
- 'query', 'search_title', 'search_content'],
- _datas: {articles: [], selected_article: null,
- filter: 'unread', filter_type: null, filter_id: null,
- display_search: false, query: null,
- search_title: true, search_content: false},
- getAll: function() {
- return this._datas;
- },
- getRequestFilter: function(display_search) {
- var filters = {'filter': this._datas.filter,
- 'filter_type': this._datas.filter_type,
- 'filter_id': this._datas.filter_id,
- };
- if(display_search || (display_search == undefined && this._datas.display_search)) {
- filters.query = this._datas.query;
- filters.search_title = this._datas.search_title;
- filters.search_content = this._datas.search_content;
- };
- return filters;
- },
- getArticles: function() {
- var key = null;
- var id = null;
- if (this._datas.filter_type) {
- key = this._datas.filter_type;
- id = this._datas.filter_id;
- }
- return this._datas.articles
- .map(function(article) {
- if(article.article_id == this._datas.selected_article) {
- article.selected = true;
- } else if(article.selected) {
- article.selected = false;
- }
- return article;
- }.bind(this))
- .filter(function(article) {
- return (article.selected || ((!key || article[key] == id)
- && (this._datas.filter == 'all'
- || (this._datas.filter == 'unread' && !article.read)
- || (this._datas.filter == 'liked' && article.liked))));
- }.bind(this));
-
- },
- setArticles: function(articles) {
- if(articles || articles == []) {
- this._datas.articles = articles;
- return true;
- }
- return false;
- },
- registerFilter: function(action) {
- var changed = false;
- this.filter_whitelist.map(function(key) {
- if(key in action && this._datas[key] != action[key]) {
- changed = true;
- this._datas[key] = action[key];
- }
- }.bind(this));
- return changed;
- },
- emitChange: function() {
- this.emit(CHANGE_EVENT);
- },
- addChangeListener: function(callback) {
- this.on(CHANGE_EVENT, callback);
- },
- removeChangeListener: function(callback) {
- this.removeListener(CHANGE_EVENT, callback);
- },
-});
-
-
-MiddlePanelStore.dispatchToken = JarrDispatcher.register(function(action) {
- var changed = false;
- if (action.type == ActionTypes.RELOAD_MIDDLE_PANEL
- || action.type == ActionTypes.PARENT_FILTER
- || action.type == ActionTypes.MIDDLE_PANEL_FILTER) {
- changed = MiddlePanelStore.registerFilter(action);
- changed = MiddlePanelStore.setArticles(action.articles) || changed;
- } else if (action.type == ActionTypes.MARK_ALL_AS_READ) {
- changed = MiddlePanelStore.registerFilter(action);
- for(var i in action.articles) {
- action.articles[i].read = true;
- }
- changed = MiddlePanelStore.setArticles(action.articles) || changed;
- } else if (action.type == ActionTypes.CHANGE_ATTR) {
- var attr = action.attribute;
- var val = action.value_bool;
- action.articles.map(function(article) {
- for (var i in MiddlePanelStore._datas.articles) {
- if(MiddlePanelStore._datas.articles[i].article_id == article.article_id) {
- if (MiddlePanelStore._datas.articles[i][attr] != val) {
- MiddlePanelStore._datas.articles[i][attr] = val;
- // avoiding redraw if not filter, display won't change anyway
- if(MiddlePanelStore._datas.filter != 'all') {
- changed = true;
- }
- }
- break;
- }
- }
- });
- } else if (action.type == ActionTypes.LOAD_ARTICLE) {
- changed = true;
- MiddlePanelStore._datas.selected_article = action.article.id;
- for (var i in MiddlePanelStore._datas.articles) {
- if(MiddlePanelStore._datas.articles[i].article_id == action.article.id) {
- MiddlePanelStore._datas.articles[i].read = true;
- break;
- }
- }
- }
- if(changed) {MiddlePanelStore.emitChange();}
-});
-
-module.exports = MiddlePanelStore;
diff --git a/src/web/js/stores/RightPanelStore.js b/src/web/js/stores/RightPanelStore.js
deleted file mode 100644
index 6c268dfd..00000000
--- a/src/web/js/stores/RightPanelStore.js
+++ /dev/null
@@ -1,77 +0,0 @@
-var JarrDispatcher = require('../dispatcher/JarrDispatcher');
-var ActionTypes = require('../constants/JarrConstants');
-var EventEmitter = require('events').EventEmitter;
-var CHANGE_EVENT = 'change_middle_panel';
-var assign = require('object-assign');
-var MenuStore = require('../stores/MenuStore');
-
-
-var RightPanelStore = assign({}, EventEmitter.prototype, {
- category: null,
- feed: null,
- article: null,
- current: null,
- getAll: function() {
- return {category: this.category, feed: this.feed,
- article: this.article, current: this.current};
- },
- emitChange: function() {
- this.emit(CHANGE_EVENT);
- },
- addChangeListener: function(callback) {
- this.on(CHANGE_EVENT, callback);
- },
- removeChangeListener: function(callback) {
- this.removeListener(CHANGE_EVENT, callback);
- },
-});
-
-
-RightPanelStore.dispatchToken = JarrDispatcher.register(function(action) {
- switch(action.type) {
- case ActionTypes.PARENT_FILTER:
- RightPanelStore.article = null;
- if(action.filter_id == null) {
- RightPanelStore.category = null;
- RightPanelStore.feed = null;
- RightPanelStore.current = null;
- } else if(action.filter_type == 'category_id') {
- RightPanelStore.category = MenuStore._datas.categories[action.filter_id];
- RightPanelStore.feed = null;
- RightPanelStore.current = 'category';
- RightPanelStore.emitChange();
- } else {
-
- RightPanelStore.feed = MenuStore._datas.feeds[action.filter_id];
- RightPanelStore.category = MenuStore._datas.categories[RightPanelStore.feed.category_id];
- RightPanelStore.current = 'feed';
- RightPanelStore.emitChange();
- }
- break;
- case ActionTypes.LOAD_ARTICLE:
- RightPanelStore.feed = MenuStore._datas.feeds[action.article.feed_id];
- RightPanelStore.category = MenuStore._datas.categories[action.article.category_id];
- RightPanelStore.article = action.article;
- RightPanelStore.current = 'article';
- RightPanelStore.emitChange();
- break;
- case ActionTypes.RELOAD_MENU:
- RightPanelStore.article = null;
- if(RightPanelStore.category && !(RightPanelStore.category.id.toString() in action.categories)) {
- RightPanelStore.category = null;
- RightPanelStore.current = null;
- }
- if(RightPanelStore.feed && !(RightPanelStore.feed.id.toString() in action.feeds)) {
- RightPanelStore.feed = null;
- RightPanelStore.current = null;
- }
- if(RightPanelStore.current == 'article') {
- RightPanelStore.current = null;
- }
- RightPanelStore.emitChange();
- default:
- // pass
- }
-});
-
-module.exports = RightPanelStore;
diff --git a/src/web/js/stores/__tests__/TodoStore-test.js b/src/web/js/stores/__tests__/TodoStore-test.js
deleted file mode 100644
index 6da6cd3c..00000000
--- a/src/web/js/stores/__tests__/TodoStore-test.js
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (c) 2014-2015, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- * TodoStore-test
- */
-
-jest.dontMock('../../constants/TodoConstants');
-jest.dontMock('../TodoStore');
-jest.dontMock('object-assign');
-
-describe('TodoStore', function() {
-
- var TodoConstants = require('../../constants/TodoConstants');
- var AppDispatcher;
- var TodoStore;
- var callback;
-
- // mock actions
- var actionTodoCreate = {
- actionType: TodoConstants.TODO_CREATE,
- text: 'foo'
- };
- var actionTodoDestroy = {
- actionType: TodoConstants.TODO_DESTROY,
- id: 'replace me in test'
- };
-
- beforeEach(function() {
- AppDispatcher = require('../../dispatcher/AppDispatcher');
- TodoStore = require('../TodoStore');
- callback = AppDispatcher.register.mock.calls[0][0];
- });
-
- it('registers a callback with the dispatcher', function() {
- expect(AppDispatcher.register.mock.calls.length).toBe(1);
- });
-
- it('should initialize with no to-do items', function() {
- var all = TodoStore.getAll();
- expect(all).toEqual({});
- });
-
- it('creates a to-do item', function() {
- callback(actionTodoCreate);
- var all = TodoStore.getAll();
- var keys = Object.keys(all);
- expect(keys.length).toBe(1);
- expect(all[keys[0]].text).toEqual('foo');
- });
-
- it('destroys a to-do item', function() {
- callback(actionTodoCreate);
- var all = TodoStore.getAll();
- var keys = Object.keys(all);
- expect(keys.length).toBe(1);
- actionTodoDestroy.id = keys[0];
- callback(actionTodoDestroy);
- expect(all[keys[0]]).toBeUndefined();
- });
-
- it('can determine whether all to-do items are complete', function() {
- var i = 0;
- for (; i < 3; i++) {
- callback(actionTodoCreate);
- }
- expect(Object.keys(TodoStore.getAll()).length).toBe(3);
- expect(TodoStore.areAllComplete()).toBe(false);
-
- var all = TodoStore.getAll();
- for (key in all) {
- callback({
- actionType: TodoConstants.TODO_COMPLETE,
- id: key
- });
- }
- expect(TodoStore.areAllComplete()).toBe(true);
-
- callback({
- actionType: TodoConstants.TODO_UNDO_COMPLETE,
- id: key
- });
- expect(TodoStore.areAllComplete()).toBe(false);
- });
-
-});
bgstack15