Skip to content
46 changes: 44 additions & 2 deletions src/AC/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import {
INCREMENT, DELETE_ARTICLE, CHANGE_DATE_RANGE, CHANGE_SELECTION, ADD_COMMENT, LOAD_ALL_ARTICLES, LOAD_ARTICLE,
START, SUCCESS, FAIL
INCREMENT,
DELETE_ARTICLE,
CHANGE_DATE_RANGE,
CHANGE_SELECTION,
ADD_COMMENT,
LOAD_ALL_ARTICLES,
LOAD_ARTICLE,
START,
SUCCESS,
FAIL,
LOAD_COMMENT,
LOAD_COMMENT_TO_CASHE
} from '../constants'

export function increment() {
Expand Down Expand Up @@ -72,4 +82,36 @@ export function loadArticle(id) {

}, 1000)
}
}



export function loadCommentForArticle(idArticle) {

return (dispatch) => {
dispatch({
type: LOAD_COMMENT + START,
payload: { idArticle }
})
setTimeout(() => {
fetch(`/api/comment?article=${idArticle}`)
.then(res => res.json())
.then(response => dispatch({
type: LOAD_COMMENT + SUCCESS,
payload: { idArticle, response }
}))
}, 1000)


}

}

export function loadCommentToCashe(CasheComment, id) {

return {
type: LOAD_COMMENT_TO_CASHE,
payload: { CasheComment, id }
}

}
4 changes: 2 additions & 2 deletions src/components/Article/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import CSSTransition from 'react-addons-css-transition-group'
import {connect} from 'react-redux'
import CommentList from '../CommentList'
import Loader from '../common/Loader'
import {deleteArticle, loadArticle} from '../../AC'
import { deleteArticle, loadArticle} from '../../AC'
import './style.css'

class Article extends PureComponent {
Expand Down Expand Up @@ -68,7 +68,7 @@ class Article extends PureComponent {
return (
<div>
<section>{article.text}</section>
<CommentList article = {article} ref = {this.setCommentsRef} key = {this.state.count}/>
<CommentList article = {article} ref = {this.setCommentsRef} key = {this.state.count}/>
</div>
)

Expand Down
29 changes: 18 additions & 11 deletions src/components/Comment.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import React from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import {createCommentSelector} from '../selectors'
import {commentSelectorId} from '../selectors'

function Comment({comment}) {



return (
<div>
{comment.text} <b>by {comment.user}</b>
Expand All @@ -13,20 +16,24 @@ function Comment({comment}) {

Comment.propTypes = {
id: PropTypes.string,
//from connect

comment: PropTypes.shape({
text: PropTypes.string.isRequired,
user: PropTypes.string
}).isRequired
}

const createMapStateToProps = () => {
const commentSelector = createCommentSelector()
return (state, ownProps) => {
return {
comment: commentSelector(state, ownProps)
}
}
}
// const createMapStateToProps = () => {
// const commentSelector = createCommentSelector()
// return (state, ownProps) => {
// return {
// comment: commentSelector(state, ownProps)
// }
// }
// }

export default connect(createMapStateToProps)(Comment)
export default connect ( ( state, ownProps ) => {
return {
comment: commentSelectorId( state, ownProps )
}
} ) (Comment)
55 changes: 49 additions & 6 deletions src/components/CommentList.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,30 @@ import CommentForm from './CommentForm'
import Comment from './Comment'
import toggleOpen from '../decorators/toggleOpen'

import { connect } from 'react-redux'
import { loadCommentForArticle , loadCommentToCashe } from '../AC'
import {commentsSelector, commentsLoadingSelector, commentsSelectorCashe} from '../selectors'

import Loader from './common/Loader'
class CommentList extends Component {
static propTypes = {
article: PropTypes.object.isRequired,
//from toggleOpen decorator
isOpen: PropTypes.bool,
toggleOpen: PropTypes.func
}



componentWillReceiveProps({ isOpen, article, loadCommentForArticle }){

if( !this.props.isOpen && isOpen && !article.get('commentInCashe') ) loadCommentForArticle( article.id )


}

render() {
const {isOpen, toggleOpen} = this.props
const {isOpen, toggleOpen , comments } = this.props
const text = isOpen ? 'hide comments' : 'show comments'
return (
<div>
Expand All @@ -24,23 +38,52 @@ class CommentList extends Component {
}

getBody() {
const {article: { comments, id }, isOpen} = this.props
if (!isOpen) return null
const { comments , article, isOpen, loading , loadCommentToCashe } = this.props
console.log(comments)

if( !article.get('commentInCashe') ) {
console.log( "before" + article.get('commentInCashe'))
loadCommentToCashe(comments, article.get('id'))
console.log( "after" + article.get('commentInCashe'))
// сейчас коменты в кеше
console.log(article.comments)
}





if (!isOpen) return null
if (loading) return <Loader />
const body = comments.length ? (
<ul>
{comments.map(id => <li key = {id}><Comment id = {id} /></li>)}
{comments.map(comment => <li key = {comment['id']}><Comment id = {comment['id']} /></li>)}
Copy link
Owner

Choose a reason for hiding this comment

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

вот этого хода я совсем не осознал) зачем доставать id из коммента, чтоб потом по нему снова найти этот коммент из стора?

</ul>
) : <h3>No comments yet</h3>

return (
<div>
{body}
<CommentForm articleId = {id} />
<CommentForm articleId = {article.get('id')} />
</div>
)
}
}


export default toggleOpen(CommentList)
export default connect( (state, ownProps ) => {
const { article } = ownProps
console.log(article.get('commentInCashe'))
if (! article.get('commentInCashe') ) {
return {
comments : commentsSelector(state),
loading: commentsLoadingSelector(state)
}
}

return {
comments : commentsSelectorCashe(state, ownProps ),
loading: commentsLoadingSelector(state)
}

}, { loadCommentForArticle, loadCommentToCashe } ) (toggleOpen(CommentList))
6 changes: 5 additions & 1 deletion src/constants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ export const CHANGE_SELECTION = 'CHANGE_SELECTION'
export const CHANGE_DATE_RANGE = 'CHANGE_DATE_RANGE'

export const ADD_COMMENT = 'ADD_COMMENT'
export const LOAD_COMMENT = 'LOAD_COMMENT'


export const START = '_START'
export const SUCCESS = '_SUCCESS'
export const FAIL = '_FAIL'
export const FAIL = '_FAIL'

export const LOAD_COMMENT_TO_CASHE = 'LOAD_COMMENT_TO_CASHE'
27 changes: 23 additions & 4 deletions src/reducer/articles.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { Map, Record } from 'immutable'
import { DELETE_ARTICLE, ADD_COMMENT, LOAD_ALL_ARTICLES, LOAD_ARTICLE, START, SUCCESS, FAIL } from '../constants'
import {arrToMap} from './utils'
import { DELETE_ARTICLE, ADD_COMMENT, LOAD_ALL_ARTICLES, LOAD_ARTICLE, START, SUCCESS, FAIL, LOAD_COMMENT_TO_CASHE } from '../constants'
import { arrToMap } from './utils'

const ArticleRecord = Record({
id: null,
title: null,
text: null,
date: null,
loading: false,
comments: []
comments: [],
commentInCashe: false
})

const ReducerRecord = Record({
Expand Down Expand Up @@ -50,7 +51,25 @@ export default (articles = new ReducerRecord(), action) => {

case LOAD_ARTICLE + SUCCESS:
return articles.setIn(['entities', payload.id], new ArticleRecord(payload.response))
}


case LOAD_COMMENT_TO_CASHE:
{

// console.log(payload.CasheComment)
// console.log(payload.id)
// articles: articles.updateIn(
// ['entities', payload.id, 'commentInCashe'],
// true
// )

return articles.updateIn(
['entities', payload.id, 'comments'],
(comments) => payload.CasheComment
).setIn(['entities', payload.id, 'commentInCashe'], true)
}

}

return articles
}
46 changes: 38 additions & 8 deletions src/reducer/comments.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,47 @@
import { ADD_COMMENT } from '../constants'
import {normalizedComments} from '../fixtures'
import {arrToMap} from './utils'
import { ADD_COMMENT, LOAD_COMMENT, START, SUCCESS } from '../constants'

export default (state = arrToMap(normalizedComments), action) => {
const { type, payload, randomId } = action
import { arrToMap } from './utils'
import { Map, Record } from 'immutable'



const CommentsRecord = Record({
id: null,
text: null,
user: null,
loading: false
})

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


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

switch (type) {
case ADD_COMMENT:
return state.set(randomId, {
return comments.setIn(['entities', randomId], new CommentsRecord({
...payload.comment,
id: randomId
})
}))

case LOAD_COMMENT + START:
return comments.set('loading', true)
Copy link
Owner

Choose a reason for hiding this comment

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

здесь не достаточно повесить loading на весь comments, ведь ты для конкрентной статьи загружаешь


case LOAD_COMMENT + SUCCESS:
{
console.log(payload.response)
return comments
.set('loading', false)
.set('loaded', true)
Copy link
Owner

Choose a reason for hiding this comment

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

Почему loaded === true, ведь ты загрузил только для одной статьи?

.set('entities', arrToMap(payload.response, CommentsRecord))
Copy link
Owner

Choose a reason for hiding this comment

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

сейчас загружая комменты для одной статьи ты перезатираешь прошлые; используй .mergeIn

}
}

return state
return comments
}
27 changes: 23 additions & 4 deletions src/selectors/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import {createSelector} from 'reselect'
import { createSelector } from 'reselect'
import comments from '../reducer/comments';
import articles from '../reducer/articles';

export const articlesMapSelector = state => state.articles.entities


export const articlesLoadingSelector = state => state.articles.loading
export const filtersSelector = state => state.filters
export const commentListSelector = state => state.comments
Expand All @@ -9,7 +13,7 @@ export const idSelector = (_, props) => props.id
export const articlesSelector = createSelector(articlesMapSelector, articles => articles.valueSeq().toArray())

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

return articles.filter(article => {
const published = Date.parse(article.date)
Expand All @@ -18,6 +22,21 @@ export const filtratedArticlesSelector = createSelector(articlesSelector, filter
})
})

export const createCommentSelector = () => createSelector(commentListSelector, idSelector, (comments, id) => {
return comments.get(id)
// export const createCommentSelector = () => createSelector(commentListSelector, idSelector, (comments, id) => {
// return comments.get(id)
// })

export const commentsMapSelector = state => state.comments.entities
export const commentsSelector = createSelector(commentsMapSelector, comments => comments.valueSeq().toArray())


export const commentsLoadingSelector = state => state.comments.loading

export const commentSelectorId = createSelector(commentsSelector, idSelector, (comments, id) => {
return comments.find(comment => comment.id == id)
})

/// непонятно как получить комеенты кеша которые там есть в
export const commentsSelectorCasheArt = createSelector(articlesSelector, idSelector, (articles, id) => {
return (articles.find(article => article.id == id)).comments
})