Add front-end bits
This commit is contained in:
parent
2c8c5e5635
commit
5bec9ce5ee
12 changed files with 87 additions and 16 deletions
|
|
@ -31,6 +31,7 @@ class App extends React.Component {
|
|||
post={this.props.post}
|
||||
rule={this.props.rule}
|
||||
category={this.props.category}
|
||||
selectedType={this.props.selectedType}
|
||||
feedUrl={this.props.feedUrl}
|
||||
subredditUrl={this.props.subredditUrl}
|
||||
timelineUrl={this.props.timelineUrl}
|
||||
|
|
@ -62,6 +63,7 @@ const mapStateToProps = state => {
|
|||
error,
|
||||
rule,
|
||||
post: state.selected.post,
|
||||
selectedType: state.selected.item.type,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -96,6 +96,23 @@ export const toggleSaved = (post, token) => {
|
|||
};
|
||||
};
|
||||
|
||||
// TODO add tests
|
||||
export const fetchSavedPosts = (next = false) => {
|
||||
return dispatch => {
|
||||
dispatch(requestPosts());
|
||||
|
||||
const url = next ? next : '/api/posts/?saved=true';
|
||||
|
||||
return fetch(url)
|
||||
.then(response => response.json())
|
||||
.then(posts => dispatch(receivePosts(posts.results, posts.next)))
|
||||
.catch(error => {
|
||||
dispatch(receivePosts([]));
|
||||
dispatch(handleAPIError(error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export const fetchPostsBySection = (section, next = false) => {
|
||||
return dispatch => {
|
||||
if (section.unread === 0) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ import { receiveRule, requestRule } from './rules.js';
|
|||
import { CATEGORY_TYPE, RULE_TYPE } from '../constants.js';
|
||||
|
||||
export const MARK_SECTION_READ = 'MARK_SECTION_READ';
|
||||
export const SELECT_SAVED = 'SELECT_SAVED';
|
||||
|
||||
export const selectSaved = () => ({ type: SELECT_SAVED });
|
||||
|
||||
export const markSectionRead = section => ({
|
||||
type: MARK_SECTION_READ,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { unSelectPost, markPostRead, toggleSaved } from '../actions/posts.js';
|
|||
import {
|
||||
CATEGORY_TYPE,
|
||||
RULE_TYPE,
|
||||
SAVED_TYPE,
|
||||
FEED,
|
||||
SUBREDDIT,
|
||||
TWITTER_TIMELINE,
|
||||
|
|
@ -21,7 +22,7 @@ class PostModal extends React.Component {
|
|||
const markPostRead = this.props.markPostRead;
|
||||
const token = Cookies.get('csrftoken');
|
||||
|
||||
if (this.props.autoMarking && !post.read) {
|
||||
if (this.props.autoMarking && this.props.selectedType != SAVED_TYPE && !post.read) {
|
||||
this.readTimer = setTimeout(markPostRead, 3000, post, token);
|
||||
}
|
||||
|
||||
|
|
@ -51,7 +52,8 @@ class PostModal extends React.Component {
|
|||
const token = Cookies.get('csrftoken');
|
||||
const publicationDate = formatDatetime(post.publicationDate);
|
||||
const titleClassName = post.read ? 'post__title post__title--read' : 'post__title';
|
||||
const readButtonDisabled = post.read || this.props.isUpdating;
|
||||
const readButtonDisabled =
|
||||
post.read || this.props.isUpdating || this.props.selectedType === SAVED_TYPE;
|
||||
const savedIconClass = post.saved ? 'saved-icon saved-icon--saved' : 'saved-icon';
|
||||
|
||||
let ruleUrl = '';
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { connect } from 'react-redux';
|
|||
import {
|
||||
CATEGORY_TYPE,
|
||||
RULE_TYPE,
|
||||
SAVED_TYPE,
|
||||
FEED,
|
||||
SUBREDDIT,
|
||||
TWITTER_TIMELINE,
|
||||
|
|
@ -43,7 +44,7 @@ class PostItem extends React.Component {
|
|||
<span className="posts-info__date" title={publicationDate}>
|
||||
{publicationDate} {this.props.timezone} {post.author && `By ${post.author}`}
|
||||
</span>
|
||||
{this.props.selected.type == CATEGORY_TYPE && (
|
||||
{[CATEGORY_TYPE, SAVED_TYPE].includes(this.props.selected.type) && (
|
||||
<span className="badge">
|
||||
<a href={ruleUrl} target="_blank" rel="noopener noreferrer">
|
||||
{rule.name}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ import React from 'react';
|
|||
import { connect } from 'react-redux';
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
import { fetchPostsBySection } from '../../actions/posts.js';
|
||||
import { fetchPostsBySection, fetchSavedPosts } from '../../actions/posts.js';
|
||||
import { SAVED_TYPE } from '../../constants.js';
|
||||
import { filterPosts } from './filters.js';
|
||||
|
||||
import LoadingIndicator from '../../../../components/LoadingIndicator.js';
|
||||
|
|
@ -33,11 +34,15 @@ class PostList extends React.Component {
|
|||
}
|
||||
|
||||
paginate() {
|
||||
this.props.fetchPostsBySection(this.props.selected, this.props.next);
|
||||
if (this.props.selected.type === SAVED_TYPE) {
|
||||
this.props.fetchSavedPosts(this.props.next);
|
||||
} else {
|
||||
this.props.fetchPostsBySection(this.props.selected, this.props.next);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const postItems = this.props.postsBySection.map((item, index) => {
|
||||
const postItems = this.props.postsByType.map((item, index) => {
|
||||
return (
|
||||
<PostItem
|
||||
key={index}
|
||||
|
|
@ -55,7 +60,7 @@ class PostList extends React.Component {
|
|||
return (
|
||||
<div className="post-message">
|
||||
<div className="post-message__block">
|
||||
<i class="fas fa-arrow-left" />
|
||||
<i className="fas fa-arrow-left" />
|
||||
<p className="post-message__text">Select an item to show its unread posts</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -83,7 +88,7 @@ class PostList extends React.Component {
|
|||
|
||||
const mapStateToProps = state => ({
|
||||
isFetching: state.posts.isFetching,
|
||||
postsBySection: filterPosts(state),
|
||||
postsByType: filterPosts(state),
|
||||
next: state.selected.next,
|
||||
lastReached: state.selected.lastReached,
|
||||
selected: state.selected.item,
|
||||
|
|
@ -91,6 +96,7 @@ const mapStateToProps = state => ({
|
|||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
fetchPostsBySection: (rule, next = false) => dispatch(fetchPostsBySection(rule, next)),
|
||||
fetchSavedPosts: (next = false) => dispatch(fetchSavedPosts(next)),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(PostList);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { CATEGORY_TYPE, RULE_TYPE } from '../../constants.js';
|
||||
import { CATEGORY_TYPE, RULE_TYPE, SAVED_TYPE } from '../../constants.js';
|
||||
|
||||
const isEmpty = (object = {}) => {
|
||||
return Object.keys(object).length === 0;
|
||||
|
|
@ -17,6 +17,10 @@ const sortOrdering = (firstPost, secondPost) => {
|
|||
return dateOrdering;
|
||||
};
|
||||
|
||||
const savedOrdering = (firstPost, secondPost) => {
|
||||
return new Date(firstPost.publicationDate) < new Date(secondPost.publicationDate);
|
||||
};
|
||||
|
||||
export const filterPostsByRule = (rule = {}, posts = []) => {
|
||||
const filteredPosts = posts.filter(post => {
|
||||
return post.rule === rule.id;
|
||||
|
|
@ -45,15 +49,24 @@ export const filterPostsByCategory = (category = {}, rules = [], posts = []) =>
|
|||
return sortedPosts;
|
||||
};
|
||||
|
||||
export const filterPostsBySaved = (rules = [], posts = []) => {
|
||||
const filteredPosts = posts.filter(post => post.saved);
|
||||
return filteredPosts
|
||||
.map(post => ({ ...post, rule: { ...rules.find(rule => rule.id === post.rule) } }))
|
||||
.sort(savedOrdering);
|
||||
};
|
||||
|
||||
export const filterPosts = state => {
|
||||
const posts = Object.values({ ...state.posts.items });
|
||||
const rules = Object.values({ ...state.rules.items });
|
||||
|
||||
switch (state.selected.item.type) {
|
||||
case CATEGORY_TYPE:
|
||||
const rules = Object.values({ ...state.rules.items });
|
||||
return filterPostsByCategory({ ...state.selected.item }, rules, posts);
|
||||
case RULE_TYPE:
|
||||
return filterPostsByRule({ ...state.selected.item }, posts);
|
||||
case SAVED_TYPE:
|
||||
return filterPostsBySaved(rules, posts);
|
||||
}
|
||||
|
||||
return [];
|
||||
|
|
|
|||
|
|
@ -1,13 +1,27 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { fetchSavedPosts } from '../../actions/posts.js';
|
||||
import { selectSaved } from '../../actions/selected.js';
|
||||
import { SAVED_TYPE } from '../../constants.js';
|
||||
|
||||
class SavedItem extends React.Component {
|
||||
handleSelect() {
|
||||
this.props.selectSaved();
|
||||
this.props.fetchSavedPosts();
|
||||
}
|
||||
|
||||
render() {
|
||||
const className =
|
||||
this.props.selected.type === SAVED_TYPE
|
||||
? 'sidebar__container sidebar__container--selected'
|
||||
: 'sidebar__container';
|
||||
|
||||
return (
|
||||
<li className="sidebar__item">
|
||||
<div className="sidebar__container">
|
||||
<div className={className}>
|
||||
<span className="sidebar__icon saved-icon" />
|
||||
<div className="sidebar__text">
|
||||
<div className="sidebar__text" onClick={() => this.handleSelect()}>
|
||||
<span>Saved posts</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -16,4 +30,9 @@ class SavedItem extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export default connect()(SavedItem);
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
selectSaved: () => dispatch(selectSaved()),
|
||||
fetchSavedPosts: () => dispatch(fetchSavedPosts()),
|
||||
});
|
||||
|
||||
export default connect(null, mapDispatchToProps)(SavedItem);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class Sidebar extends React.Component {
|
|||
)}
|
||||
|
||||
<ul className="sidebar__nav">
|
||||
<SavedItem />
|
||||
<SavedItem selected={this.props.selected.item} />
|
||||
{categoryItems}
|
||||
</ul>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
export const RULE_TYPE = 'RULE';
|
||||
export const CATEGORY_TYPE = 'CATEGORY';
|
||||
export const SAVED_TYPE = 'SAVED';
|
||||
|
||||
export const SUBREDDIT = 'subreddit';
|
||||
export const FEED = 'feed';
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import { objectsFromArray } from '../../../utils.js';
|
|||
import { CATEGORY_TYPE, RULE_TYPE } from '../constants.js';
|
||||
|
||||
import {
|
||||
SELECT_POST,
|
||||
MARKING_POST,
|
||||
MARK_POST_READ,
|
||||
RECEIVE_POST,
|
||||
|
|
|
|||
|
|
@ -9,8 +9,9 @@ import {
|
|||
UNSELECT_POST,
|
||||
} from '../actions/posts.js';
|
||||
|
||||
import { MARK_SECTION_READ } from '../actions/selected.js';
|
||||
import { MARK_SECTION_READ, SELECT_SAVED } from '../actions/selected.js';
|
||||
import { MARK_POST_READ } from '../actions/posts.js';
|
||||
import { SAVED_TYPE } from '../constants.js';
|
||||
|
||||
const defaultState = { item: {}, next: false, lastReached: false, post: {} };
|
||||
|
||||
|
|
@ -47,6 +48,13 @@ export const selected = (state = { ...defaultState }, action) => {
|
|||
next: false,
|
||||
lastReached: false,
|
||||
};
|
||||
case SELECT_SAVED:
|
||||
return {
|
||||
...state,
|
||||
item: { type: SAVED_TYPE },
|
||||
next: false,
|
||||
lastReached: false,
|
||||
};
|
||||
case RECEIVE_POSTS:
|
||||
return {
|
||||
...state,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue