Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/AC/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
INCREMENT, DELETE_ARTICLE, CHANGE_DATE_RANGE, CHANGE_SELECTION, ADD_COMMENT,
LOAD_ALL_ARTICLES, LOAD_ARTICLE, LOAD_ARTICLE_COMMENTS, START, SUCCESS, FAIL
LOAD_ALL_COMMENTS, LOAD_ALL_ARTICLES, LOAD_ARTICLE, LOAD_ARTICLE_COMMENTS,
START, SUCCESS, FAIL
} from '../constants'

export function increment() {
Expand Down Expand Up @@ -73,4 +74,12 @@ export function loadArticleComments(articleId) {
payload: { articleId },
callAPI: `/api/comment?article=${articleId}`
}
}
}

export function loadAllComments(page, {limit, offset}) {
return {
type: LOAD_ALL_COMMENTS,
payload: {page},
callAPI: `/api/comment?limit=${limit}&offset=${offset}`
}
}
4 changes: 4 additions & 0 deletions src/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import ArticleListPage from './routes/ArticleList'
import UserForm from './UserForm'
import FiltersPage from './routes/Filters'
import CounterPage from './routes/Counter'
import CommentListPage from './routes/CommentList'
import { Route, Switch, NavLink } from 'react-router-dom'

class App extends Component {
Expand All @@ -14,13 +15,16 @@ class App extends Component {
<li><NavLink to = "/articles" activeStyle = {{ color: 'red' }}>Articles</NavLink></li>
<li><NavLink to = "/filters" activeStyle = {{ color: 'red' }}>Filters</NavLink></li>
<li><NavLink to = "/counter" activeStyle = {{ color: 'red' }}>Counter</NavLink></li>
<li><NavLink to = "/comments" activeStyle = {{ color: 'red' }}>Comments</NavLink></li>
</ul>
<UserForm />
<Switch>
<Route path = "/counter" component = {CounterPage} exact/>
<Route path = "/filters" component = {FiltersPage}/>
<Route path = "/articles/new" render = {() => <h2>Add new Article form</h2>}/>
<Route path = "/articles" component = {ArticleListPage}/>
<Route path = "/comments/:page" component = {CommentListPage}/>
<Route path = "/comments/" component = {CommentListPage}/>
<Route path = "*" render = {() => <h1>Nor found</h1>}/>
</Switch>
</div>
Expand Down
44 changes: 44 additions & 0 deletions src/components/Pagination.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import { Route, Switch, NavLink } from 'react-router-dom'
import {loadAllComments} from '../AC'
import {contentSelector} from '../selectors'

class Pagination extends Component {
static propTypes = {
currentPage: PropTypes.string,
pages: PropTypes.number,
perPage: PropTypes.number,
contentLoading: PropTypes.bool,
contentLoaded: PropTypes.bool
}

componentWillReceiveProps(nextProps) {
if (this.props.currentPage === nextProps.currentPage) return
const limit = nextProps.perPage
const currentPage = nextProps.currentPage
const offset = (currentPage-1) * limit
if (!nextProps.contentLoading && !nextProps.contentLoaded) {
this.props.loadAllComments(currentPage || 1, {limit, offset})
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

я бы передавал page, а уже тут превращал это в offset и limit

}
}

render() {
const {pages} = this.props
const body = new Array(pages).fill(0).map((_, i) => i+1).map(page => <li key = {page}>
<NavLink to = {`/comments/${page}`} activeStyle = {{ color: 'red' }}>{page}</NavLink>
</li>)
return (
<div>
<h1>Pagination</h1>
<ul>
{body}
</ul>
</div>
)
}

}

export default connect(null, {loadAllComments})(Pagination)
49 changes: 49 additions & 0 deletions src/components/routes/CommentList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React, { Component } from 'react'
import {connect} from 'react-redux'
import Comment from '../Comment'
import Pagination from '../Pagination'
import Loader from '../common/Loader'
import {Route} from 'react-router-dom'
import {loadAllComments} from '../../AC'
import {contentSelector} from '../../selectors'

class CommentListPage extends Component {
state = {
perPage: 5
}

componentDidMount() {
const limit = this.state.perPage
const currentPage = this.props.match.params.page
const offset = (currentPage-1) * limit
this.props.loadAllComments(currentPage || 1, {limit, offset})
}

render() {
const { total, comments} = this.props
const {perPage} = this.state

const body = comments.contentLoaded && comments.records.length ? (
<ul>
{comments.records.map(id => <li key = {id}><Comment id = {id} /></li>)}
</ul>
) : comments.contentLoading && !comments.contentLoaded ? <Loader/> : <h3>No comments yet</h3>
return (
<div>
<h2>Comment list</h2>
{body}
<Pagination
currentPage = {this.props.match.params.page}
pages = {Math.ceil(total/perPage)}
perPage = {this.state.perPage}
contentLoading = {comments.contentLoading}
contentLoaded = {comments.contentLoaded}/>
</div>
)
}
}

export default connect((state, props) => ({
comments: contentSelector(state, props),
total: state.pages.total
}), {loadAllComments} )(CommentListPage)
1 change: 1 addition & 0 deletions src/constants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const CHANGE_SELECTION = 'CHANGE_SELECTION'
export const CHANGE_DATE_RANGE = 'CHANGE_DATE_RANGE'

export const ADD_COMMENT = 'ADD_COMMENT'
export const LOAD_ALL_COMMENTS = 'LOAD_ALL_COMMENTS'

export const START = '_START'
export const SUCCESS = '_SUCCESS'
Expand Down
6 changes: 4 additions & 2 deletions src/reducer/comments.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ADD_COMMENT, LOAD_ARTICLE_COMMENTS, SUCCESS } from '../constants'
import { ADD_COMMENT, LOAD_ARTICLE_COMMENTS, START, SUCCESS, LOAD_ALL_COMMENTS } from '../constants'
import { arrToMap } from './utils'
import { OrderedMap, Record } from 'immutable'

Expand All @@ -12,7 +12,6 @@ const ReducerState = Record({
entities: new OrderedMap({})
})


export default (state = new ReducerState(), action) => {
const { type, payload, response, randomId } = action

Expand All @@ -22,6 +21,9 @@ export default (state = new ReducerState(), action) => {

case LOAD_ARTICLE_COMMENTS + SUCCESS:
return state.mergeIn(['entities'], arrToMap(response, CommentRecord))

case LOAD_ALL_COMMENTS + SUCCESS:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Почему ALL_COMMENTS?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В самом начале так назвал, но действительно лучше название связать с page

return state.mergeIn(['entities'], arrToMap(response.records, CommentRecord))
}

return state
Expand Down
4 changes: 3 additions & 1 deletion src/reducer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import counterReducer from './counter'
import articles from './articles'
import comments from './comments'
import filters from './filters'
import pagination from './pagination'

export default combineReducers({
counter: counterReducer,
articles, comments, filters
articles, comments, filters,
pages: pagination
})
35 changes: 35 additions & 0 deletions src/reducer/pagination.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Record } from 'immutable'
import { LOAD_ALL_COMMENTS, START, SUCCESS, FAIL } from '../constants'
import {arrToMap} from './utils'

const PagesRecord = Record({
records: [],
contentLoading: false,
contentLoaded: false
})

const ReducerRecord = Record({
entities: arrToMap([], PagesRecord),
total: 0,
loading: false,
loaded: false,
error: null
})

export default (pages = new ReducerRecord(), action) => {
const { type, payload, response, error } = action

switch (type) {
case LOAD_ALL_COMMENTS + START:
return pages.setIn(['entities', payload.page, 'contentLoading'], true)

case LOAD_ALL_COMMENTS + SUCCESS:
return pages
.setIn(['entities', payload.page, 'contentLoading'], false)
.setIn(['entities', payload.page, 'contentLoaded'], true)
.setIn(['entities', payload.page, 'records'], arrToMap(response.records, PagesRecord).keySeq())
.set('total', response.total)
}

return pages
}
10 changes: 9 additions & 1 deletion src/selectors/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,18 @@ import {createSelector} from 'reselect'
export const articlesMapSelector = state => state.articles.entities
export const articlesLoadingSelector = state => state.articles.loading
export const filtersSelector = state => state.filters
export const commentMapSelector = state => state.comments.get('entities')
export const idSelector = (_, props) => props.id
export const commentMapSelector = state => state.comments.get('entities')
export const contentMapSelector = state => state.pages.entities
export const pageSelector = (_, props) => props.match.params.page || 1

export const articlesSelector = createSelector(articlesMapSelector, articles => articles.valueSeq().toArray())
export const contentSelector = createSelector(contentMapSelector, pageSelector, (content, page) => {
return content.has(page) ? content.get(page).toJS() : []
})
export const loadingContentSelector = (state, props) => {
return state.pages.entities.size ? state.pages.entities.get(props.match.params.page).contentLoading : true
}

export const filtratedArticlesSelector = createSelector(articlesSelector, filtersSelector, (articles, filters) => {
const {selected, dateRange: {from, to}} = filters
Expand Down