Skip to content
Open

HW-5 #75

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
8,877 changes: 8,877 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion src/AC/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {INCREMENT, DELETE_ARTICLE, CHANGE_DATE_RANGE, CHANGE_SELECTION} from '../constants'
import {INCREMENT, DELETE_ARTICLE, CHANGE_DATE_RANGE, CHANGE_SELECTION, ADD_COMMENT} from '../constants'

export function increment() {
return {
Expand Down Expand Up @@ -26,3 +26,11 @@ export function changeSelection(selected) {
payload: { selected }
}
}

export function addComment(comment, articleId) {
return {
type: ADD_COMMENT,
payload: {...comment, articleId},
randomId: true
}
}
22 changes: 19 additions & 3 deletions src/components/Article/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,21 @@ import {findDOMNode} from 'react-dom'
import PropTypes from 'prop-types'
import CSSTransition from 'react-addons-css-transition-group'
import {connect} from 'react-redux'
import {createArticleSelector} from '../../selectors'
import CommentList from '../CommentList'
import {deleteArticle} from '../../AC'
import './style.css'

class Article extends PureComponent {
static propTypes = {
id: PropTypes.string.isRequired,

article: PropTypes.shape({
title: PropTypes.string.isRequired,
text: PropTypes.string,
comments: PropTypes.array
}).isRequired,

isOpen: PropTypes.bool,
toggleOpen: PropTypes.func
}
Expand All @@ -25,15 +29,18 @@ class Article extends PureComponent {
foo: 'bar',
count: 0
}

}

render() {
console.log('---', 'rerendering')
const {article, isOpen, toggleOpen} = this.props

const {article, id, isOpen, toggleOpen} = this.props

const body = isOpen && (
<div>
<section>{article.text}</section>
<CommentList comments = {article.comments} ref = {this.setCommentsRef} key = {this.state.count}/>
<CommentList article = {article} ref = {this.setCommentsRef} key = {this.state.count}/>
</div>
)
return (
Expand Down Expand Up @@ -85,5 +92,14 @@ class Article extends PureComponent {

}

const createMapStateToProps = () => {
const articleSelector = createArticleSelector()
return (state, ownProps) => {
return {
article: articleSelector(state, ownProps)
}
}
}


export default connect(null, { deleteArticle })(Article)
export default connect(createMapStateToProps, { deleteArticle })(Article)
11 changes: 6 additions & 5 deletions src/components/ArticleList.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ import {connect} from 'react-redux'
import {filtratedArticlesSelector} from '../selectors'

class ArticleList extends Accordion {


render() {
console.log('---', 'rerendering article list')
const {articles} = this.props
if (!articles.length) return <h3>No Articles</h3>
const articleElements = articles.map((article) => <li key={article.id}>
<Article article={article}
isOpen={article.id === this.state.openItemId}
toggleOpen={this.toggleOpenItemMemoized(article.id)}
const articleElements = articles.map((id) => <li key={id}>
<Article id={id}
isOpen={id === this.state.openItemId}
toggleOpen={this.toggleOpenItemMemoized(id)}
/>
</li>)
return (
Expand All @@ -24,7 +26,6 @@ class ArticleList extends Accordion {
}
}


ArticleList.defaultProps = {
articles: []
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Comment.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ function Comment({comment}) {
}

Comment.propTypes = {
id: PropTypes.string,
id: PropTypes.string.isRequired,
//from connect
comment: PropTypes.shape({
text: PropTypes.string.isRequired,
Expand Down
14 changes: 9 additions & 5 deletions src/components/CommentForm/index.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import React, { Component } from 'react'
import React, { Component, PropTypes } from 'react'
import './style.css'

class CommentForm extends Component {
static propTypes = {
};

state = {
user: '',
text: ''
text: '',
user: ''
}

render() {
Expand All @@ -26,6 +24,12 @@ class CommentForm extends Component {

handleSubmit = ev => {
ev.preventDefault()
const { addComment, articleId } = this.props
addComment({
user: this.state.user,
text: this.state.text
}, articleId)

this.setState({
user: '',
text: ''
Expand Down
36 changes: 29 additions & 7 deletions src/components/CommentList.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,21 @@ import PropTypes from 'prop-types'
import CommentForm from './CommentForm'
import Comment from './Comment'
import toggleOpen from '../decorators/toggleOpen'
import {addComment} from '../AC'
import { connect } from 'react-redux'


class CommentList extends Component {
static propTypes = {
comments: PropTypes.array.isRequired,
comments: PropTypes.array,
//from toggleOpen decorator
isOpen: PropTypes.bool,
toggleOpen: PropTypes.func
}

render() {
const {isOpen, toggleOpen} = this.props
const {comments, isOpen, toggleOpen} = this.props

const text = isOpen ? 'hide comments' : 'show comments'
return (
<div>
Expand All @@ -24,23 +28,41 @@ class CommentList extends Component {
}

getBody() {
const {comments, isOpen} = this.props
const {comments, isOpen, article, addComment} = this.props

if (!isOpen) return null

const body = comments.length ? (
const body = comments && comments.length !== 0 ? (
<ul>
{comments.map(id => <li key = {id}><Comment id = {id} /></li>)}
{comments.map(comment => <li key = {comment.id}><Comment id = {comment.id} /></li>)}
</ul>
) : <h3>No comments yet</h3>

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

CommentList.defaultProps = {
comments: []
}


export default toggleOpen(CommentList)

export default connect((state, { article }) => {
if (article.comments) {
return {
comments: article.comments.map(id => state.comments[id])
}
} else {
return {
comments: []
}
}
}, {
addComment
})(toggleOpen(CommentList))
10 changes: 6 additions & 4 deletions src/components/Filters/Select.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ import 'react-select/dist/react-select.css'

class SelectFilter extends Component {
static propTypes = {
articles: PropTypes.array.isRequired
articles: PropTypes.object.isRequired
};

handleChange = selected => this.props.changeSelection(selected.map(option => option.value))

render() {
const { articles, selected } = this.props
const options = articles.map(article => ({
label: article.title,
value: article.id
console.log("articles", articles )

const options = Object.keys(articles).map(id => ({
label: articles[id].title,
value: id
}))

return <Select
Expand Down
4 changes: 3 additions & 1 deletion src/constants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ export const INCREMENT = 'INCREMENT'
export const DELETE_ARTICLE = 'DELETE_ARTICLE'

export const CHANGE_SELECTION = 'CHANGE_SELECTION'
export const CHANGE_DATE_RANGE = 'CHANGE_DATE_RANGE'
export const CHANGE_DATE_RANGE = 'CHANGE_DATE_RANGE'

export const ADD_COMMENT = 'ADD_COMMENT'
11 changes: 11 additions & 0 deletions src/middlewares/idGenerator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default store => next => action => {

const { randomId, ...rest} = action
if (!randomId) return next(action)

next({
...rest,
randomId: Math.random().toString(36).substr(2, 9)
})
}

29 changes: 25 additions & 4 deletions src/reducer/articles.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,33 @@
import { DELETE_ARTICLE } from '../constants'
import { DELETE_ARTICLE, ADD_COMMENT } from '../constants'
import {normalizedArticles as defaultArticles} from '../fixtures'

export default (articlesState = defaultArticles, action) => {
const { type, payload } = action
const articlesMap = defaultArticles.reduce((acc, article) => ({
...acc,
[article.id]: article
}), {})

export default (articlesState = articlesMap, action) => {
const { type, payload, randomId } = action

switch (type) {
case DELETE_ARTICLE:
return articlesState.filter(article => article.id !== payload.id)
const obj = Object.keys(articlesState).reduce((obj, key) => {
if (key !== payload.id) return {...obj, [key]: articlesState[key] }
return obj
}, {})

return obj

case ADD_COMMENT:
const articleId = payload.articleId
const article = articlesState[articleId]

const updatedArticle = Object.assign({}, article, {
comments: (article.comments || []).concat(randomId)
})

return Object.assign({}, articlesState, {[articleId]: updatedArticle})

}

return articlesState
Expand Down
11 changes: 9 additions & 2 deletions src/reducer/comments.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { } from '../constants'
import { ADD_COMMENT } from '../constants'
import {normalizedComments as defaultComments} from '../fixtures'

const commentsMap = defaultComments.reduce((acc, comment) => ({
Expand All @@ -7,10 +7,17 @@ const commentsMap = defaultComments.reduce((acc, comment) => ({
}), {})

export default (state = commentsMap, action) => {
const { type } = action
const { type, payload, randomId } = action

switch (type) {
case ADD_COMMENT:

return Object.assign({}, state, {
[randomId]: {
id: randomId,
...payload
}
})
}

return state
Expand Down
13 changes: 9 additions & 4 deletions src/selectors/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ export const filtersSelector = state => state.filters
export const commentsSelector = state => state.comments
export const idSelector = (_, props) => props.id

export const createArticleSelector = () => createSelector(articlesSelector, idSelector, (articles, id) => {
console.log('---', 'searching for article', id)
return articles[id]
})

export const createCommentSelector = () => createSelector(commentsSelector, idSelector, (comments, id) => {
console.log('---', 'searching for comment', id)
return comments[id]
Expand All @@ -13,10 +18,10 @@ export const createCommentSelector = () => createSelector(commentsSelector, idSe
export const filtratedArticlesSelector = createSelector(articlesSelector, filtersSelector, (articles, filters) => {
console.log('---', 'computing filters')
const {selected, dateRange: {from, to}} = filters

return articles.filter(article => {
const published = Date.parse(article.date)
return (!selected.length || selected.includes(article.id)) &&
return Object.keys(articles).filter(id => {
const published = Date.parse(articles[id].date)
return (!selected.length || selected.includes(id)) &&
(!from || !to || (published > from && published < to))
})
})
3 changes: 2 additions & 1 deletion src/store/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {createStore, applyMiddleware, compose} from 'redux'
import rootReducer from '../reducer'
import logger from '../middlewares/logger'
import idGenerator from '../middlewares/idGenerator'

const composeEnhancers =
typeof window === 'object' &&
Expand All @@ -10,7 +11,7 @@ const composeEnhancers =
}) : compose

const enhancer = composeEnhancers(
applyMiddleware(logger)
applyMiddleware(idGenerator, logger)
)
const store = createStore(rootReducer, enhancer)

Expand Down