diff --git a/src/AC/index.js b/src/AC/index.js
index dda2dd6..ec3bc53 100644
--- a/src/AC/index.js
+++ b/src/AC/index.js
@@ -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, OPEN_ARTICLE} from '../constants'
export function increment() {
return {
@@ -6,6 +6,13 @@ export function increment() {
}
}
+export function openArticle(id) {
+ return {
+ type: OPEN_ARTICLE,
+ payload: { id }
+ }
+}
+
export function deleteArticle(id) {
return {
type: DELETE_ARTICLE,
@@ -26,3 +33,10 @@ export function changeSelection(selected) {
payload: { selected }
}
}
+
+export function addComment(comment) {
+ return {
+ type: ADD_COMMENT,
+ payload: { comment }
+ }
+}
\ No newline at end of file
diff --git a/src/components/ArticleList.js b/src/components/ArticleList.js
index bc1c261..b2af4c5 100644
--- a/src/components/ArticleList.js
+++ b/src/components/ArticleList.js
@@ -10,9 +10,9 @@ class ArticleList extends Accordion {
console.log('---', 'rerendering article list')
const {articles} = this.props
if (!articles.length) return
No Articles
- const articleElements = articles.map((article) =>
+ const articleElements = Object.values(articles).map((article) =>
)
@@ -36,6 +36,7 @@ ArticleList.propTypes = {
export default connect(state => {
console.log('---', 'connect updated')
return {
+ openItemId: state.openArticleId,
articles: filtratedArticlesSelector(state)
}
})(ArticleList)
\ No newline at end of file
diff --git a/src/components/CommentForm/index.js b/src/components/CommentForm/index.js
index 3685a10..daf3dda 100644
--- a/src/components/CommentForm/index.js
+++ b/src/components/CommentForm/index.js
@@ -1,4 +1,7 @@
import React, { Component } from 'react'
+import {connect} from 'react-redux'
+import {addComment} from '../../AC'
+
import './style.css'
class CommentForm extends Component {
@@ -26,6 +29,13 @@ class CommentForm extends Component {
handleSubmit = ev => {
ev.preventDefault()
+
+ const {addComment} = this.props
+ addComment({
+ user: this.state.user,
+ text: this.state.text
+ })
+
this.setState({
user: '',
text: ''
@@ -58,4 +68,4 @@ const limits = {
}
}
-export default CommentForm
\ No newline at end of file
+export default connect(null, { addComment })(CommentForm)
\ No newline at end of file
diff --git a/src/components/Filters/Select.js b/src/components/Filters/Select.js
index 1a91857..a9252b6 100644
--- a/src/components/Filters/Select.js
+++ b/src/components/Filters/Select.js
@@ -8,14 +8,14 @@ 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 => ({
+ const options = Object.values(articles).map(article => ({
label: article.title,
value: article.id
}))
diff --git a/src/components/common/Accordion.js b/src/components/common/Accordion.js
index 1d63067..bd3a160 100644
--- a/src/components/common/Accordion.js
+++ b/src/components/common/Accordion.js
@@ -1,4 +1,6 @@
import React, { Component } from 'react'
+import {openArticle} from '../../AC'
+import store from '../../store'
class Accordion extends Component {
state = {
@@ -6,9 +8,7 @@ class Accordion extends Component {
}
toggleOpenItem = openItemId => ev => {
- this.setState({
- openItemId: openItemId === this.state.openItemId ? null : openItemId
- })
+ store.dispatch(openArticle(openItemId === this.props.openItemId ? null : openItemId))
}
toggleOpenItemMemoized = (openItemId) => {
diff --git a/src/constants/index.js b/src/constants/index.js
index a2f0a80..27f2e6f 100644
--- a/src/constants/index.js
+++ b/src/constants/index.js
@@ -1,6 +1,9 @@
export const INCREMENT = 'INCREMENT'
+export const OPEN_ARTICLE = 'OPEN_ARTICLE'
export const DELETE_ARTICLE = 'DELETE_ARTICLE'
export const CHANGE_SELECTION = 'CHANGE_SELECTION'
-export const CHANGE_DATE_RANGE = 'CHANGE_DATE_RANGE'
\ No newline at end of file
+export const CHANGE_DATE_RANGE = 'CHANGE_DATE_RANGE'
+
+export const ADD_COMMENT = 'ADD_COMMENT'
\ No newline at end of file
diff --git a/src/middlewares/idGenerator.js b/src/middlewares/idGenerator.js
new file mode 100644
index 0000000..ac39b75
--- /dev/null
+++ b/src/middlewares/idGenerator.js
@@ -0,0 +1,25 @@
+import {ADD_COMMENT} from '../constants'
+
+export default store => next => action => {
+ function genId(len) {
+ function getRandomChar() {
+ let number = Math.round(Math.random() * 15);
+ return number < 10 ? String.fromCharCode(97 + number) : String.fromCharCode(39 + number)
+ }
+
+ let result = getRandomChar();
+ for (let i = 0; i < len - 1; i++) {
+ result += getRandomChar();
+ }
+ return result;
+ }
+
+ switch (action.type) {
+ case ADD_COMMENT:
+ action.payload.id = genId(10)
+ action.payload.articleId = store.getState().openArticleId
+ break;
+ }
+
+ next(action)
+}
\ No newline at end of file
diff --git a/src/reducer/articles.js b/src/reducer/articles.js
index 480a522..bf3312a 100644
--- a/src/reducer/articles.js
+++ b/src/reducer/articles.js
@@ -1,12 +1,30 @@
-import { DELETE_ARTICLE } from '../constants'
+import { DELETE_ARTICLE, ADD_COMMENT, OPEN_ARTICLE } from '../constants'
import {normalizedArticles as defaultArticles} from '../fixtures'
-export default (articlesState = defaultArticles, action) => {
+const articlesMap = defaultArticles.reduce((acc, article) => {
+ article.comments = article.comments != null ? article.comments : []
+ return {
+ ...acc,
+ [article.id]: article
+ }
+}, {})
+
+export default (articlesState = articlesMap, action) => {
const { type, payload } = action
switch (type) {
case DELETE_ARTICLE:
- return articlesState.filter(article => article.id !== payload.id)
+ articlesState = {...articlesState}
+ delete articlesState[payload.id]
+ return articlesState
+ case ADD_COMMENT:
+ let article = {...articlesState[payload.articleId]}
+ article.comments = [...article.comments]
+ article.comments.push(payload.id)
+ return {
+ ...articlesState,
+ [payload.articleId]: article
+ }
}
return articlesState
diff --git a/src/reducer/comments.js b/src/reducer/comments.js
index 30bd10c..b2d7308 100644
--- a/src/reducer/comments.js
+++ b/src/reducer/comments.js
@@ -1,4 +1,4 @@
-import { } from '../constants'
+import { ADD_COMMENT } from '../constants'
import {normalizedComments as defaultComments} from '../fixtures'
const commentsMap = defaultComments.reduce((acc, comment) => ({
@@ -10,7 +10,15 @@ export default (state = commentsMap, action) => {
const { type } = action
switch (type) {
-
+ case ADD_COMMENT:
+ state = {
+ ...state,
+ [action.payload.id]: {
+ ...action.payload.comment,
+ id: action.payload.id
+ }
+ }
+ return state;
}
return state
diff --git a/src/reducer/index.js b/src/reducer/index.js
index 25a0b0b..634f091 100644
--- a/src/reducer/index.js
+++ b/src/reducer/index.js
@@ -3,8 +3,10 @@ import counterReducer from './counter'
import articles from './articles'
import comments from './comments'
import filters from './filters'
+import openArticle from './openArticle';
export default combineReducers({
counter: counterReducer,
- articles, comments, filters
+ articles, comments, filters,
+ openArticleId: openArticle
})
\ No newline at end of file
diff --git a/src/reducer/openArticle.js b/src/reducer/openArticle.js
new file mode 100644
index 0000000..fdb2415
--- /dev/null
+++ b/src/reducer/openArticle.js
@@ -0,0 +1,5 @@
+import {OPEN_ARTICLE} from '../constants'
+
+export default (openArticleId = null, action) => {
+ return action.type === OPEN_ARTICLE ? action.payload.id : openArticleId
+}
\ No newline at end of file
diff --git a/src/selectors/index.js b/src/selectors/index.js
index 4e3172b..df03af0 100644
--- a/src/selectors/index.js
+++ b/src/selectors/index.js
@@ -14,7 +14,7 @@ export const filtratedArticlesSelector = createSelector(articlesSelector, filter
console.log('---', 'computing filters')
const {selected, dateRange: {from, to}} = filters
- return articles.filter(article => {
+ return Object.values(articles).filter(article => {
const published = Date.parse(article.date)
return (!selected.length || selected.includes(article.id)) &&
(!from || !to || (published > from && published < to))
diff --git a/src/store/index.js b/src/store/index.js
index f83d367..733e3ba 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -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' &&
@@ -10,7 +11,7 @@ const composeEnhancers =
}) : compose
const enhancer = composeEnhancers(
- applyMiddleware(logger)
+ applyMiddleware(logger, idGenerator)
)
const store = createStore(rootReducer, enhancer)