diff options
Diffstat (limited to 'src/web/js/components')
-rw-r--r-- | src/web/js/components/MainApp.react.js | 29 | ||||
-rw-r--r-- | src/web/js/components/Menu.react.js | 305 | ||||
-rw-r--r-- | src/web/js/components/MiddlePanel.react.js | 267 | ||||
-rw-r--r-- | src/web/js/components/Navbar.react.js | 138 | ||||
-rw-r--r-- | src/web/js/components/RightPanel.react.js | 463 | ||||
-rw-r--r-- | src/web/js/components/time.react.js | 15 |
6 files changed, 0 insertions, 1217 deletions
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; |