diff options
Diffstat (limited to 'src/web/js/components')
-rw-r--r-- | src/web/js/components/MainApp.react.js | 18 | ||||
-rw-r--r-- | src/web/js/components/Menu.react.js | 119 | ||||
-rw-r--r-- | src/web/js/components/MiddlePanel.react.js | 103 |
3 files changed, 240 insertions, 0 deletions
diff --git a/src/web/js/components/MainApp.react.js b/src/web/js/components/MainApp.react.js new file mode 100644 index 00000000..743c9510 --- /dev/null +++ b/src/web/js/components/MainApp.react.js @@ -0,0 +1,18 @@ +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> + ); + }, +}); + +module.exports = MainApp; diff --git a/src/web/js/components/Menu.react.js b/src/web/js/components/Menu.react.js new file mode 100644 index 00000000..caf8c3a8 --- /dev/null +++ b/src/web/js/components/Menu.react.js @@ -0,0 +1,119 @@ +var React = require('react'); +var MenuStore = require('../stores/MenuStore'); +var MenuActions = require('../actions/MenuActions'); +var MiddlePanelActions = require('../actions/MiddlePanelActions'); + +var FeedItem = React.createClass({ + propTypes: {feed_id: React.PropTypes.number.isRequired, + title: React.PropTypes.string.isRequired, + unread: React.PropTypes.number.isRequired, + icon_url: React.PropTypes.string, + }, + getInitialState: function() { + return {feed_id: this.props.feed_id, + title: this.props.title, + unread: this.props.unread, + icon_url: this.props.icon_url, + }; + }, + render: function() { + var unread = undefined; + var icon = undefined; + if(this.state.icon_url){ + icon = (<img width="16px" src={this.state.icon_url} />); + } else { + icon = (<span className="glyphicon glyphicon-ban-circle" />); + } + if(this.state.unread){ + unread = ( + <span className="badge pull-right"> + {this.state.unread} + </span> + ); + } + return (<li onMouseDown={this.handleClick}> + {icon} {this.state.title} {unread} + </li> + ); + }, + handleClick: function() { + MiddlePanelActions.setFeedFilter(this.state.feed_id); + }, +}); + +var Category = React.createClass({ + propTypes: {category_id: React.PropTypes.number.isRequired, + name: React.PropTypes.string.isRequired, + feeds: React.PropTypes.array.isRequired, + unread: React.PropTypes.number.isRequired, + }, + getInitialState: function() { + return {category_id: this.props.category_id, + name: this.props.name, + feeds: this.props.feeds, + unread: this.props.unread, + }; + }, + render: function() { + unread = undefined; + if(this.state.unread){ + unread = ( + <span className="badge pull-right"> + {this.state.unread} + </span> + ); + } + return (<div> + <h3 onMouseDown={this.handleClick}> + {this.state.name} {unread} + </h3> + <ul className="nav nav-sidebar"> + {this.state.feeds.map(function(feed){ + return <FeedItem key={"feed" + feed.id} + feed_id={feed.id} + title={feed.title} + unread={feed.unread} + icon_url={feed.icon_url} />;})} + </ul> + </div> + ); + }, + handleClick: function() { + MiddlePanelActions.setCategoryFilter(this.state.category_id); + }, +}); + +var Menu = React.createClass({ + getInitialState: function() { + return {categories: [], all_unread_count: 0}; + }, + render: function() { + return (<div id="sidebar" data-spy="affix" role="navigation" + className="col-md-2 sidebar sidebar-offcanvas pre-scrollable hidden-sm hidden-xs affix"> + {this.state.categories.map(function(category){ + return (<Category key={"cat" + category.id} + category_id={category.id} + feeds={category.feeds} + name={category.name} + unread={category.unread} />); + })} + + </div> + ); + }, + + componentDidMount: function() { + MenuActions.reload(); + MenuStore.addChangeListener(this._onChange); + }, + componentWillUnmount: function() { + MenuStore.removeChangeListener(this._onChange); + }, + _onChange: function() { + var datas = MenuStore.getAll(); + this.setState({categories: datas.categories, + all_unread_count: datas.all_unread_count}); + }, +}); + +module.exports = Menu; diff --git a/src/web/js/components/MiddlePanel.react.js b/src/web/js/components/MiddlePanel.react.js new file mode 100644 index 00000000..51d582c0 --- /dev/null +++ b/src/web/js/components/MiddlePanel.react.js @@ -0,0 +1,103 @@ +var React = require('react'); +var MiddlePanelStore = require('../stores/MiddlePanelStore'); +var MiddlePanelActions = require('../actions/MiddlePanelActions'); + +var TableLine = React.createClass({ + propTypes: {article_id: React.PropTypes.number.isRequired, + feed_title: React.PropTypes.string.isRequired, + icon_url: React.PropTypes.string, + title: React.PropTypes.string.isRequired, + date: React.PropTypes.string.isRequired, + read: React.PropTypes.bool.isRequired, + liked: React.PropTypes.bool.isRequired, + }, + getInitialState: function() { + return {article_id: this.props.article_id, + title: this.props.title, + icon_url: this.props.icon_url, + feed_title: this.props.feed_title, + date: this.props.date, + read: this.props.read, + liked: this.props.liked, + }; + }, + render: function() { + var read = this.state.read ? 'r' : ''; + var liked = this.state.liked ? 'l' : ''; + var icon = undefined; + if(this.state.icon_url){ + icon = (<img width="16px" src={this.state.icon_url} />); + } else { + icon = (<span className="glyphicon glyphicon-ban-circle" />); + } + return ( + <tr> + <td>{icon}{liked}</td> + <td> + <a href={'/redirect/' + this.state.article_id}> + {this.state.feed_title} + </a> + </td> + <td> + <a href={'/article/' + this.state.article_id}> + {this.state.title} + </a> + </td> + <td>{this.state.date}</td> + </tr> + ); + }, +}); + +var TableBody = React.createClass({ + propTypes: {articles: React.PropTypes.array.isRequired, + }, + getInitialState: function() { + return {articles: this.props.articles, + }; + }, + render: function() { + return (<div className="table-responsive"> + <table className="table table-striped strict-table"> + <tbody> + {this.state.articles.map(function(article){ + return (<TableLine key={"article" + article.article_id} + title={article.title} + icon_url={article.icon_url} + read={article.read} + liked={article.liked} + date={article.date} + article_id={article.article_id} + feed_title={article.feed_title} />);})} + </tbody> + </table> + </div> + ); + } +}); + +var MiddlePanel = React.createClass({ + getInitialState: function() { + return {articles: []}; + }, + render: function() { + var body = null; + if(this.state.articles.length) { + body = (<TableBody articles={this.state.articles} />); + } + return (<div className="col-md-offset-2 col-md-10 main">{body}</div>); + }, + componentDidMount: function() { + MiddlePanelActions.reload(); + MiddlePanelStore.addChangeListener(this._onChange); + }, + componentWillUnmount: function() { + MiddlePanelStore.removeChangeListener(this._onChange); + }, + _onChange: function() { + var datas = MiddlePanelStore.getAll(); + this.setState({articles: datas.articles}); + }, +}); + +module.exports = MiddlePanel; |