diff --git a/src/ac/index.js b/src/ac/index.js
index b93bb22..81b24ad 100644
--- a/src/ac/index.js
+++ b/src/ac/index.js
@@ -8,7 +8,8 @@ import {
LOAD_ARTICLE,
SUCCESS,
FAIL,
- START
+ START,
+ LOAD_COMMENTS
} from '../constants'
import { fetchData } from './service'
@@ -88,3 +89,28 @@ export function loadArticle(id) {
)
}
}
+
+export function loadComments(articleId) {
+ return (dispatch) => {
+ dispatch({
+ type: LOAD_COMMENTS + START,
+ payload: { articleId }
+ })
+
+ fetchData(`comment?article=${articleId}`)
+ .then((response) =>
+ dispatch({
+ type: LOAD_COMMENTS + SUCCESS,
+ payload: { articleId },
+ response
+ })
+ )
+ .catch((error) =>
+ dispatch({
+ type: LOAD_COMMENTS + FAIL,
+ payload: { articleId },
+ error
+ })
+ )
+ }
+}
diff --git a/src/components/comment-list/index.js b/src/components/comment-list/index.js
index ebe51f9..5224f32 100644
--- a/src/components/comment-list/index.js
+++ b/src/components/comment-list/index.js
@@ -5,13 +5,36 @@ import Comment from '../comment'
import CommentForm from '../comment-form'
import toggleOpen from '../../decorators/toggleOpen'
import './style.css'
+import { loadComments } from '../../ac'
+import {
+ loadingCommentsSelector,
+ isCommentsLoadedSelector
+} from '../../selectors'
+import { connect } from 'react-redux'
+import Loader from '../common/loader'
class CommentList extends Component {
static propTypes = {
article: PropTypes.object.isRequired,
//from toggleOpen decorator
isOpen: PropTypes.bool,
- toggleOpen: PropTypes.func
+ toggleOpen: PropTypes.func,
+ loadComments: PropTypes.func,
+ isCommentsLoaded: PropTypes.bool,
+ loading: PropTypes.bool
+ }
+
+ componentDidUpdate(oldProps) {
+ const {
+ isOpen,
+ isCommentsLoaded,
+ article,
+ loadComments,
+ loading
+ } = this.props
+
+ if (!oldProps.isOpen && isOpen && !loading && !isCommentsLoaded)
+ loadComments(article.id)
}
render() {
@@ -36,13 +59,16 @@ class CommentList extends Component {
getBody() {
const {
article: { comments = [], id },
- isOpen
+ isOpen,
+ loading,
+ isCommentsLoaded
} = this.props
if (!isOpen) return null
+ if (loading) return
return (
- {comments.length ? (
+ {comments.length && isCommentsLoaded ? (
this.getComments()
) : (
No comments yet
@@ -65,4 +91,15 @@ class CommentList extends Component {
}
}
-export default toggleOpen(CommentList)
+const createMapStateToProps = () => {
+ const isCommentsLoaded = isCommentsLoadedSelector()
+
+ return (state, ownProps) => ({
+ loading: loadingCommentsSelector(state),
+ isCommentsLoaded: isCommentsLoaded(state, ownProps)
+ })
+}
+
+export default toggleOpen(
+ connect(createMapStateToProps, { loadComments })(CommentList)
+)
diff --git a/src/constants/index.js b/src/constants/index.js
index 6266326..9ef403c 100644
--- a/src/constants/index.js
+++ b/src/constants/index.js
@@ -8,6 +8,7 @@ export const CHANGE_SELECTION = 'CHANGE_SELECTION'
export const CHANGE_DATE_RANGE = 'CHANGE_DATE_RANGE'
export const ADD_COMMENT = 'ADD_COMMENT'
+export const LOAD_COMMENTS = 'LOAD_COMMENTS'
export const START = '_START'
export const SUCCESS = '_SUCCESS'
diff --git a/src/reducer/comments.js b/src/reducer/comments.js
index e4645f7..c24ac2f 100644
--- a/src/reducer/comments.js
+++ b/src/reducer/comments.js
@@ -1,16 +1,48 @@
-import { ADD_COMMENT } from '../constants'
-import { normalizedComments } from '../fixtures'
+import { ADD_COMMENT, LOAD_COMMENTS, START, SUCCESS, FAIL } from '../constants'
import { arrToMap } from './utils'
+import { Record } from 'immutable'
-export default (state = arrToMap(normalizedComments), action) => {
- const { type, payload, randomId } = action
+const CommentModel = new Record({
+ id: null,
+ user: null,
+ text: null
+})
+
+const ReducerRecord = new Record({
+ entities: arrToMap([], CommentModel),
+ loading: false,
+ error: null
+})
+
+export default (state = new ReducerRecord(), action) => {
+ const { type, payload, randomId, response } = action
switch (type) {
case ADD_COMMENT:
- return state.set(randomId, {
- ...payload.comment,
- id: randomId
- })
+ return state.set(
+ 'entities',
+ state.get('entities').set(
+ randomId,
+ new CommentModel({
+ ...payload.comment,
+ id: randomId
+ })
+ )
+ )
+
+ case LOAD_COMMENTS + SUCCESS:
+ return state
+ .set(
+ 'entities',
+ state.get('entities').merge(arrToMap(response, CommentModel))
+ )
+ .set('loading', false)
+
+ case LOAD_COMMENTS + START:
+ return state.set('loading', true)
+
+ case LOAD_COMMENTS + FAIL:
+ return state.set('loading', false).set('error', action.error)
default:
return state
diff --git a/src/selectors/index.js b/src/selectors/index.js
index 916c01d..e5d0c93 100644
--- a/src/selectors/index.js
+++ b/src/selectors/index.js
@@ -12,7 +12,11 @@ export const articleListSelector = createSelector(
export const loadingArticlesSelector = (state) => state.articles.loading
const filtersSelector = (state) => state.filters
-const commentListSelector = (state) => state.comments
+const commentMapSelector = (state) => state.comments.entities
+const commentListSelector = createSelector(commentMapSelector, (commentsMap) =>
+ commentsMap.valueSeq().toArray()
+)
+
export const filtersSelectionSelector = createSelector(
filtersSelector,
(filters) => filters.selected
@@ -40,6 +44,22 @@ export const filtratedArticlesSelector = createSelector(
export const createCommentSelector = () => {
return createSelector(commentListSelector, idSelector, (comments, id) => {
- return comments.get(id)
+ return comments.find((comment) => comment.id === id)
})
}
+
+const commentIdsSelector = (_, props) => props.article.comments
+
+export const isCommentsLoadedSelector = () => {
+ return createSelector(
+ commentListSelector,
+ commentIdsSelector,
+ (comments, commentIds) => {
+ return commentIds.every((commentId) =>
+ comments.find((comment) => comment.id === commentId)
+ )
+ }
+ )
+}
+
+export const loadingCommentsSelector = (state) => state.comments.loading