<repo_name>oliverhager29/readable<file_sep>/src/utils/ReadableAPI.js
const api = "http://localhost:3001"
const uuidv4 = require('uuid/v4');
// Generate a unique token for storing your forum data on the backend server.
let token = localStorage.token
if (!token)
    token = localStorage.token = Math.random().toString(36).substr(-8)

const headers = {
    'Accept': 'application/json',
    'Authorization': 'Basic bmFtZTpwYXNzd29yZA=='
}

export const getAllPosts = () =>
    fetch(`${api}/posts`, { headers })
        .then(res => res.json())
        .catch(error => console.error('Error:', error))
        .then(data => data)

export const getAllCategories = () =>
    fetch(`${api}/categories`, { headers })
        .then(res => res.json())
        .catch(error => console.error('Error:', error))
        .then(data => data.categories)

export const getPostById = (postId) =>
    fetch(`${api}/posts/${postId}`, { headers })
        .then(res => res.json())
        .catch(error => console.error('Error:', error))
        .then(data => data)

export const getPostsForCategory = (category) =>
    fetch(`${api}/${category}/posts`, { headers })
        .then(res => res.json())
        .catch(error => console.error('Error:', error))
        .then(data => data)

export const getCommentById = (commentId) =>
    fetch(`${api}/comments/${commentId}`, { headers })
        .then(res => res.json())
        .catch(error => console.error('Error:', error))
        .then(data => data)

export const getCommentsForPost = (postId) =>
    fetch(`${api}/posts/${postId}/comments`, { headers })
        .then(res => res.json())
        .catch(error => console.error('Error:', error))
        .then(data => data)

export const updatePost = (post) =>
    fetch(`${api}/posts/${post.id}`, {
        method: 'PUT',
        headers: {
            ...headers,
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(post)
    }).then(res => res.json())
        .catch(error => console.error('Error:', error))
        .then(data => data)

export const updateComment = (comment) =>
    fetch(`${api}/comments/${comment.id}`, {
        method: 'PUT',
        headers: {
            ...headers,
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(comment)
    }).then(res => res.json())
        .catch(error => console.error('Error:', error))
        .then(data => data)

export const upVoteComment = (commentId) =>
    fetch(`${api}/comments/${commentId}`, {
        method: 'POST',
        headers: {
            ...headers,
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({'option': 'upVote'})
    }).then(res => res.json())
        .then(data => data)
        .catch(error => console.log(error))

export const downVoteComment = (commentId) =>
    fetch(`${api}/comments/${commentId}`, {
        method: 'POST',
        headers: {
            ...headers,
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({'option': 'downVote'})
    }).then(res => res.json())
        .then(data => data)
        .catch(error => console.log(error))

export const upVotePost = (postId) =>
    fetch(`${api}/posts/${postId}`, {
        method: 'POST',
        headers: {
            ...headers,
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({'option': 'upVote'})
    }).then(res => res.json())
        .then(data => data)
        .catch(error => console.log(error))

export const downVotePost = (postId) =>
    fetch(`${api}/posts/${postId}`, {
        method: 'POST',
        headers: {
            ...headers,
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({'option': 'downVote'})
    }).then(res => res.json())
        .catch(error => console.error('Error:', error))
        .then(data => data)

export const createPost = (post) =>
    fetch(`${api}/posts`, {
        method: 'POST',
        headers: {
            ...headers,
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(post)
    }).then(res => res.json())
        .catch(error => console.error('Error:', error))
        .then(data => data).catch()

export const createComment = (comment) =>
    fetch(`${api}/comments`, {
        method: 'POST',
        headers: {
            ...headers,
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(comment)
    }).then(res => res.json())
        .catch(error => console.error('Error:', error))
        .then(data => data)

export const deletePost = (postId) =>
    fetch(`${api}/posts/${postId}`, {
        method: 'DELETE',
        headers: {
            ...headers,
            'Content-Type': 'application/json'
        }
    }).then(res => res.json())
        .catch(error => console.error('Error:', error))
        .then(data => data)

export const deleteComment = (commentId) =>
    fetch(`${api}/comments/${commentId}`, {
        method: 'DELETE',
        headers: {
            ...headers,
            'Content-Type': 'application/json'
        }
    }).then(res => res.json())
        .catch(error => console.error('Error:', error))
        .then(data => data)

export function getId() {
    let newId=uuidv4();
    newId=newId.replace(/-/g, "")
    return newId;
}<file_sep>/src/components/PostListByCategory.js
import React, { Component } from 'react';
import {Link} from 'react-router-dom'
import Modal from 'react-modal'
import PostCreateEdit from "./PostCreateEdit";
import * as ReadableAPI from "../utils/ReadableAPI";
import {
    addPostAction, removePostAction, updatePostAction, upVotePostAction, downVotePostAction,
    addCommentAction, updateCommentAction, removeCommentAction, upVoteCommentAction, downVoteCommentAction,
    addCategoriesAction
} from '../actions'
import {connect} from "react-redux";

Modal.setAppElement('#root');

class PostListByCategory extends Component {

    static contextTypes = {
        router: () => true, // replace with PropTypes.object if you use them
    }

    state = {
        isCreatePostModalOpen: false,
        isEditPostModalOpen: [],
        isSortedByTimestamp: true,
        isSortedByVoteScore: false,
        isAscending: true
    }

    openCreatePostModal() {
        this.setState(() => ({isCreatePostModalOpen: true}))
    }

    closeCreatePostModal(){
        this.setState(() => ({isCreatePostModalOpen: false}))
    }

    openEditPostModal(index) {
        const isEditPostModalOpen = this.state.isEditPostModalOpen.map(f => false)
        isEditPostModalOpen[index] = true
        this.setState(() => ({isEditPostModalOpen: isEditPostModalOpen}))
    }

    closeEditPostModal(){
        const isEditPostModalOpen = this.state.isEditPostModalOpen.map(f => false)
        this.setState(() => ({isEditPostModalOpen: isEditPostModalOpen}))
    }

    handleDownVote = (postId, index) => {
        const {actions} = this.props
        ReadableAPI.downVotePost(postId).then(
            (post) => {
                actions.downVotePostProp({id: post.id, voteScore: post.voteScore})
            }
        )
    }

    handleUpVote = (postId, index) => {
        const {actions} = this.props
        ReadableAPI.upVotePost(postId).then(
            (post) => {
                actions.upVotePostProp({id: post.id, voteScore: post.voteScore})
            }
        )
    }

    handleDelete = (postId, index) => {
        const {actions} = this.props
        ReadableAPI.deletePost(postId).then(
            (post) => {
                actions.removePostProp(post.id)
            }
        )
    }

    handleSortByTimestamp = () => {
        let isAscending = true
        if(this.state.isSortedByTimestamp) {
            isAscending=!this.state.isAscending
        }
        this.setState({isSortedByTimestamp: true, isSortedByVoteScore: false, isAscending: isAscending})
    }

    handleSortByVoteScore = () => {
        let isAscending = true
        if(this.state.isSortedByVoteScore) {
            isAscending=!this.state.isAscending
        }
        this.setState({isSortedByTimestamp: false, isSortedByVoteScore: true, isAscending: isAscending})
    }

    constructor(props) {
        super(props)
        this.openCreatePostModal = this.openCreatePostModal.bind(this);
        this.closeCreatePostModal = this.closeCreatePostModal.bind(this);
        this.openEditPostModal = this.openEditPostModal.bind(this);
        this.closeEditPostModal = this.closeEditPostModal.bind(this);

        this.handleSortByTimestamp = this.handleSortByTimestamp.bind(this);
        this.handleSortByVoteScore = this.handleSortByVoteScore.bind(this);
    }

    render() {
        let {actions, categories, posts, comments} = this.props
        const path = this.props.location.pathname
        const category = path.substring(path.lastIndexOf('/')+1, path.length)
        if(actions==null) {
            actions = {}
        }
        if(categories==null) {
            categories = []
        }
        if(posts==null) {
            posts = {allIds: [], byId: {}, byCategory: {}}
        }
        if(comments==null) {
            comments = {allIds: [], byId: {}}
        }
        const {isSortedByTimestamp, isSortedByVoteScore, isAscending} = this.state
        const handleDownVote = this.handleDownVote
        const handleUpVote = this.handleUpVote
        const handleDelete = this.handleDelete
        const isEditPostModalOpen = this.state.isEditPostModalOpen
        const closeEditPostModal = this.closeEditPostModal
        const openEditPostModal = this.openEditPostModal
        return (
            <div>
                                <div id="categories">
                                    <h1>Posts by Categories:</h1>
                                    <table className="form">
                                        <tbody>
                                        {categories.map(function (category, index) {
                                            return (
                                                <tr key={category.name} >
                                                    <td>
                                                        <Link to={{
                                                            pathname: '/'+category.path
                                                        }}>
                                                            View Posts in Category {category.name}
                                                        </Link>
                                                    </td>
                                                </tr>
                                            )})}
                                            <tr key="all">
                                                <td>
                                                    <Link to={{
                                                        pathname: '/'
                                                    }}>
                                                        View Posts in all Categories
                                                    </Link>
                                                </td>
                                            </tr>
                                        </tbody>
                                    </table>
                                </div>
                                <div id="postsByCategory">
                                    <h1>Posts in Category {category}:</h1>
                                    <table>
                                        <thead className={"table-header"}>
                                        <tr>
                                            <th>Id</th>
                                            <th>Timestamp<button><img height='20' width='20' src={(isSortedByTimestamp?(isAscending?'./sort-up.svg':'./sort-down.svg'):'./sort-arrows-couple-pointing-up-and-down.svg')} alt="sort by timestamp" onClick={this.handleSortByTimestamp} /></button></th>
                                            <th>Title</th>
                                            <th>Body</th>
                                            <th>Author</th>
                                            <th>Category</th>
                                            <th>Vote Score<button><img height='20' width='20' src={(isSortedByVoteScore?(isAscending?'./sort-up.svg':'./sort-down.svg'):'./sort-arrows-couple-pointing-up-and-down.svg')} alt="sort by vote score" onClick={this.handleSortByVoteScore} /></button></th>
                                            <th>Comment Count</th>
                                            <th>Delete</th>
                                            <th>Down Vote</th>
                                            <th>Up Vote</th>
                                            <th>Edit Post</th>
                                        </tr>
                                        </thead>
                                        <tbody>
                                            {(posts===null || posts.allIds==null || posts.allIds.length===0 || posts.byCategory==null || posts.byCategory[category]==null || posts.byCategory[category].length===0) ?
                                                <tr><td colSpan={12}>no posts available</td></tr>
                                                :posts.byCategory[category].map(postId => posts.byId[postId] )
                                                    .filter(post => post.deleted===false)
                                                    .sort(function(a,b) {
                                                        if(isSortedByTimestamp) {
                                                            if(isAscending) {
                                                                return a.timestamp - b.timestamp
                                                            }
                                                            else {
                                                                return b.timestamp - a.timestamp
                                                            }
                                                        }
                                                        else if(isSortedByVoteScore) {
                                                            if(isAscending) {
                                                                return a.voteScore - b.voteScore
                                                            }
                                                            else {
                                                                return b.voteScore - a.voteScore
                                                            }
                                                        }
                                                        return 0
                                                    })
                                                    .map(function (post, index) {
                                                        const postId = post.id
                                                    const dateTime = new Date(post.timestamp)
                                                    const dateTimeStr=dateTime.toLocaleString()
                                                    return (
                                                        <tr key={postId} className={index%2===0?"table-row-even":"table-row-odd"}>
                                                            <td>
                                                                <Link to={{
                                                                    pathname: '/'+post.category+'/'+post.id
                                                                }}>
                                                                    {post.id}
                                                                </Link>
                                                            </td>
                                                            <td>{dateTimeStr}</td>
                                                            <td>{post.title}</td>
                                                            <td>{post.body}</td>
                                                            <td>{post.author}</td>
                                                            <td>{post.category}</td>
                                                            <td>{post.voteScore}</td>
                                                            <td>{post.commentCount}</td>
                                                            <td><button onClick={() => handleDelete(post.id, index)} name="Delete">Delete</button></td>
                                                            <td><button onClick={() => handleDownVote(post.id, index)} name="Vote Down">Vote Down</button></td>
                                                            <td><button onClick={() => handleUpVote(post.id, index)} name="Vote Up">Vote Up</button></td>
                                                            <td>
                                                                <button
                                                                    className='button'
                                                                    onClick={() => openEditPostModal(index)}>
                                                                    Edit
                                                                </button>
                                                                <div>
                                                                    <Modal
                                                                        className='modal'
                                                                        overlayClassName='overlay'
                                                                        isOpen={isEditPostModalOpen[index]}
                                                                        onRequestClose={closeEditPostModal}
                                                                        contentLabel='Edit Post'
                                                                        backdropColor = {'white'}
                                                                        backdropOpacity = {1}
                                                                        animationIn={'slideInLeft'}
                                                                        animationOut={'slideOutRight'}
                                                                    >
                                                                        {isEditPostModalOpen[index] && <PostCreateEdit closeModal={closeEditPostModal} postId={post.id}/>}
                                                                    </Modal>
                                                                </div>
                                                            </td>
                                                        </tr>
                                                    )
                                                }
                                            )
                                            }
                                        </tbody>
                                    </table>
                                </div>
                                <button
                                    className="button"
                                    onClick={this.context.router.history.goBack}>
                                    Back
                                </button>
                                <button
                                   className='button'
                                   onClick={() => this.openCreatePostModal()}>
                                   Create
                                </button>
                                <div>
                                <Modal
                                   className='modal'
                                   overlayClassName='overlay'
                                   isOpen={this.state.isCreatePostModalOpen}
                                   onRequestClose={this.closeCreatePostModal}
                                   contentLabel='Create Post'
                                   backdropColor = {'white'}
                                   backdropOpacity = {1}
                                   animationIn={'slideInLeft'}
                                   animationOut={'slideOutRight'}
                                >
                                   {this.state.isCreatePostModalOpen && <PostCreateEdit closeModal={this.closeCreatePostModal}/>}
                                </Modal>
                                </div>
            </div>
        );
    }
}

function mapStateToProps ({ forum }) {
    return {
        categories: forum.categories,
        posts: forum.posts,
        comments: forum.comments
    }
}

function mapDispatchToProps (dispatch) {
    return {
        actions: {
            addPostProp: (data) => dispatch(addPostAction(data)),
            removePostProp: (data) => dispatch(removePostAction(data)),
            updatePostProp: (data) => dispatch(updatePostAction(data)),
            upVotePostProp: (data) => dispatch(upVotePostAction(data)),
            downVotePostProp: (data) => dispatch(downVotePostAction(data)),
            addCommentProp: (data) => dispatch(addCommentAction(data)),
            removeCommentProp: (data) => dispatch(removeCommentAction(data)),
            updateCommentProp: (data) => dispatch(updateCommentAction(data)),
            upVoteCommentProp: (data) => dispatch(upVoteCommentAction(data)),
            downVoteCommentProp: (data) => dispatch(downVoteCommentAction(data)),
            addCategoriesProp: (data) => dispatch(addCategoriesAction(data))
        }
    }
}

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(PostListByCategory)
<file_sep>/README.md
Readable is a React application that implements a forum with posts and comments. Both can be created, deleted, modified and up/down voted. Each posts is assigned to one category. 

The main page consists of links to posts by category and all posts. Each posts entry has a link to post details with their associated comments, an up vote, down vote, edit and delete button. Further there is a create button to create a new post. In the post by catageory posts are displayed in a similar way. The post details page shows details of a post and its comments. It has edit, delete, up/down vote buttons for the post. It shows the lists of associated comments with up vote, down vote, delete and edit button. There is a also a create button in order to create a new comment. The create/edit posts/comment are implemented as modal dialogs.

In the root component (App) all valid routes are defined. Posts by category have a URL /{category} and post details have a URL /{category}/{post.id}. If post.id is the id of a deleted or non-existant post or  category a non-existant category then a 404 NotFound page is displayed (including a link to the home i.e. /). Same for any other invalid URL.
Further the root component has a Redux store that is propagted into the child components with the action functions.

Posts and comments can be sorted either by timestamp or vote score. The sorting can ascending or descending.

Redux store model (with sample data):
```i
    categories: [
        {
            "name": "react",
            "path": "react"
        },
        {
            "name": "redux",
            "path": "redux"
        },
        {
            "name": "udacity",
            "path": "udacity"
        }
    ],
    posts: {
        byId: {
            "8xf0y6ziyjabvozdd253nd": {
                "id": "8xf0y6ziyjabvozdd253nd",
                "timestamp": 1467166872634,
                "title": "Udacity is the best place to learn React",
                "body": "Everyone says so after all.",
                "author": "thingtwo",
                "category": "react",
                "voteScore": 6,
                "deleted": false,
                "commentCount": 2,
                "commentIds": ["894tuq4ut84ut8v4t8wun89g", "8tu4bsun805n8un48ve89"]
            },
            "6ni6ok3ym7mf1p33lnez": {
                "id": "6ni6ok3ym7mf1p33lnez",
                "timestamp": 1468479767190,
                "title": "Learn Redux in 10 minutes!",
                "body": "Just kidding. It takes more than 10 minutes to learn technology.",
                "author": "thingone",
                "category": "redux",
                "voteScore": -5,
                "deleted": false,
                "commentCount": 0,
                "commentIds": []
            }
        },
        byCategory: {
            "react" : ["8xf0y6ziyjabvozdd253nd"],
            "redux" : ["6ni6ok3ym7mf1p33lnez"],
            "udacity" : []
        },
        allIds: ["8xf0y6ziyjabvozdd253nd", "6ni6ok3ym7mf1p33lnez"]
    },
    comments: {
        byId: {
            "894tuq4ut84ut8v4t8wun89g" : {
                "id": "894tuq4ut84ut8v4t8wun89g",
                "parentId": "8xf0y6ziyjabvozdd253nd",
                "timestamp": 1468166872634,
                "body": "Hi there! I am a COMMENT.",
                "author": "thingtwo",
                "voteScore": 6,
                "deleted": false,
                "parentDeleted": false
            },
            "8tu4bsun805n8un48ve89" : {
                "id": "8tu4bsun805n8un48ve89",
                "parentId": "8xf0y6ziyjabvozdd253nd",
                "timestamp": 1469479767190,
                "body": "Comments. Are. Cool.",
                "author": "thingone",
                "voteScore": -5,
                "deleted": false,
                "parentDeleted": false
            }
        },
        allIds: ["894tuq4ut84ut8v4t8wun89g", "8tu4bsun805n8un48ve89"]
    } 
```
Instructions to run:
```
#install latest node js and npm
unzip projetc2_oliver_hager.zip
cd readable
# installs all necessary node js packages
npm install
cd src
# starts web container with React app
npm start
```
<file_sep>/src/reducers/index.js
import {
    ADD_POST,
    UPDATE_POST,
    REMOVE_POST,
    UP_VOTE_POST,
    DOWN_VOTE_POST,
    ADD_COMMENT,
    UPDATE_COMMENT,
    REMOVE_COMMENT,
    UP_VOTE_COMMENT,
    DOWN_VOTE_COMMENT,
    ADD_CATEGORIES
} from '../actions'
import { combineReducers } from 'redux';

const initialState = {
    categories: [],
    posts: {
        byId: {

        },
        byCategory: {

        },
        allIds: []
    },
    comments: {
        byId: {

        },
        allIds: []
    }
}

export function addPost(state=initialState, action) {
    if(action.type!==ADD_POST) {
        return state
    }
    let {id, title, body, author, category, timestamp, voteScore, deleted} = action
    let postIdsByCategory = state.posts.byCategory[category]
    if(postIdsByCategory==null) {
        postIdsByCategory = []
    }
    if(voteScore===null) {
        voteScore = 0
    }
    if(deleted===null) {
        deleted = false
    }
    const newPostIdsByCategory = postIdsByCategory.concat([id])
    const allPostIds = state.posts.allIds
    const newAllPostIds = allPostIds.concat([id])
    return {
        ...state,
        posts: {
            byId: {
                [id]: {
                    id,
                    title,
                    body,
                    author,
                    category,
                    timestamp,
                    voteScore,
                    deleted,
                    commentCount: 0,
                    commentIds: []
                },
                ...state.posts.byId
            },
            byCategory: {
                ...state.posts.byCategory,
                [category]: newPostIdsByCategory
            },
            allIds: newAllPostIds
        }
    }
}

export function updatePost(state=initialState, action) {
    if(action.type!==UPDATE_POST) {
        return state
    }
    const {id, title, body, author, category, timestamp} = action
    let postIdsByCategory = state.posts.byCategory[category]
    if(postIdsByCategory==null) {
        postIdsByCategory = []
    }
    const post = state.posts.byId[id]
    const allPostIds = state.posts.allIds
    let postCommentIds = post.commentIds
    if(postCommentIds==null) {
        postCommentIds = []
    }
    return {
        ...state,
        posts: {
            byId: {
                ...state.posts.byId,
                [id]: {
                    id,
                    title,
                    body,
                    author,
                    category,
                    timestamp,
                    commentIds: postCommentIds,
                    voteScore: post.voteScore,
                    deleted: post.deleted,
                    commentCount: post.commentCount
                }
            },
            byCategory: {
                ...state.posts.byCategory,
                [category]: postIdsByCategory
            },
            allIds: allPostIds
        }
    }
}

export function removePost(state=initialState, action) {
    if(action.type!==REMOVE_POST) {
        return state
    }
    const {id, category} = state.posts.byId[action.id]
    const postIdsByCategory = state.posts.byCategory[category]
    const newPostIdsByCategory = postIdsByCategory.filter(e => e!==id)
    const allPostIds = state.posts.allIds
    const newAllPostIds = allPostIds.filter(e => e!==id)
    const newCommentsById = Object.keys(state.comments.byId).map(
        k => state.comments.byId[k]
    ).filter(
        c => c.parentId!==id
    )
    const newAllCommentIds = Object.keys(newCommentsById).map(c => c.id)
    return {
        ...state,
        posts: {
            byId: {
                ...state.posts.byId,
                [id]: null
            },
            byCategory: {
                ...state.posts.byCategory,
                [category]: newPostIdsByCategory
            },
            allIds: newAllPostIds
        },
        comments: {
            byId: newCommentsById,
            allIds: newAllCommentIds
        }
    }
}

export function upVotePost(state=initialState, action) {
    if(action.type!==UP_VOTE_POST) {
        return state
    }
    const {id, voteScore} = action
    const post = state.posts.byId[id]
    const postIdsByCategory = state.posts.byCategory[post.category]
    const allPostIds = state.posts.allIds
    return {
        ...state,
        posts: {
            byId: {
                ...state.posts.byId,
                [id]: {
                    id,
                    title: post.title,
                    body: post.body,
                    author: post.author,
                    category: post.category,
                    timestamp: post.timestamp,
                    commentIds: post.commentIds,
                    voteScore: voteScore,
                    deleted: post.deleted,
                    commentCount: post.commentCount
                }
            },
            byCategory: {
                ...state.posts.byCategory,
                [post.category]: postIdsByCategory
            },
            allIds: allPostIds
        }
    }
}

export function downVotePost(state=initialState, action) {
    if(action.type!==DOWN_VOTE_POST) {
        return state
    }
    const {id, voteScore} = action
    const post = state.posts.byId[id]
    const postIdsByCategory = state.posts.byCategory[post.category]
    const allPostIds = state.posts.allIds
    return {
        ...state,
        posts: {
            byId: {
                ...state.posts.byId,
                [id]: {
                    id,
                    title: post.title,
                    body: post.body,
                    author: post.author,
                    category: post.category,
                    timestamp: post.timestamp,
                    commentIds: post.commentIds,
                    voteScore: voteScore,
                    deleted: post.deleted,
                    commentCount: post.commentCount
                }
            },
            byCategory: {
                ...state.posts.byCategory,
                [post.category]: postIdsByCategory
            },
            allIds: allPostIds
        }
    }
}

export function addComment(state=initialState, action) {
    if(action.type!==ADD_COMMENT) {
        return state
    }
    let {id, timestamp, voteScore, deleted, parentDeleted, body, author, parentId} = action
    const post = state.posts.byId[parentId]
    if(post==null) {
        return state
    }
    if(voteScore===null) {
        voteScore = 0
    }
    if(deleted===null) {
        deleted = false
    }
    if(parentDeleted===null) {
        parentDeleted = false
    }
    const allCommentIds = state.comments.allIds
    const newAllCommandIds = allCommentIds.concat([id])

    let allCommentIdsByPost = []
    if(post!=null && post.commentIds!=null) {
        allCommentIdsByPost = post.commentIds
    }
    const newAllCommentIdsByPost = allCommentIdsByPost.concat([id])

    return {
        ...state,
        posts: {
            ...state.posts,
            byId: {
                ...state.posts.byId,
                [post.id]: {
                    id: post.id,
                    title: post.title,
                    body: post.body,
                    author: post.author,
                    category: post.category,
                    timestamp: post.timestamp,
                    commentIds: newAllCommentIdsByPost,
                    voteScore: post.voteScore,
                    commentCount: newAllCommentIdsByPost.length,
                    deleted: post.deleted
                }
            }
        },
        comments: {
            byId: {
                ...state.comments.byId,
                [id]: {
                    id: id,
                    timestamp: timestamp,
                    body: body,
                    author: author,
                    parentId: parentId,
                    voteScore: voteScore,
                    deleted: deleted,
                    parentDeleted: parentDeleted
                }
            },
            allIds: newAllCommandIds
        }
    }
}

export function updateComment(state=initialState, action) {
    if(action.type!==UPDATE_COMMENT) {
        return state
    }
    const {	id, timestamp, body} = action
    const comment = state.comments.byId[id]
    const allCommentIds = state.comments.allIds
    return {
        ...state,
        comments: {
            byId: {
                ...state.comments.byId,
                [id]: {
                    id: id,
                    timestamp: timestamp,
                    body: body,
                    author: comment.author,
                    parentId: comment.parentId,
                    voteScore: comment.voteScore,
                    deleted: comment.deleted,
                    parentDeleted: comment.parentDeleted
                }
            },
            allIds: allCommentIds
        }
    }
}

export function removeComment(state=initialState, action) {
    if(action.type!==REMOVE_COMMENT) {
        return state
    }
    const {	id, parentId} = state.comments.byId[action.id]
    const newCommentsByPost = state.posts.byId[parentId].commentIds.filter(c => c!==id)
    const newAllCommentIds = state.comments.allIds.filter(c => c!==id)
    const post = state.posts.byId[parentId]
    const postIdsByCategory = state.posts.byCategory
    const allPostIds = state.posts.allIds
    return {
        ...state,
        posts: {
            byId: {
                ...state.posts.byId,
                [post.id]: {
                    id: post.id,
                    title: post.title,
                    body: post.body,
                    author: post.author,
                    category: post.category,
                    timestamp: post.timestamp,
                    commentIds: newCommentsByPost,
                    voteScore: post.voteScore,
                    commentCount: newCommentsByPost.length,
                    deleted: post.deleted
                }
            },
            byCategory: {
                postIdsByCategory
            },
            allIds: allPostIds
        },
        comments: {
            byId: {
                ...state.comments.byId,
                [id]: null
            },
            allIds: newAllCommentIds
        }
    }
}

export function upVoteComment(state=initialState, action) {
    if(action.type!==UP_VOTE_COMMENT) {
        return state
    }
    const {id, voteScore} = action
    const comment = state.comments.byId[id]
    const allCommentIds = state.comments.allIds
    return {
        ...state,
        comments: {
            byId: {
                ...state.comments.byId,
                [id]: {
                    id: id,
                    timestamp: comment.timestamp,
                    body: comment.body,
                    author: comment.author,
                    parentId: comment.parentId,
                    voteScore: voteScore,
                    deleted: comment.deleted,
                    parentDeleted: comment.parentDeleted
                }
            },
            allIds: allCommentIds
        }
    }
}

export function downVoteComment(state=initialState, action) {
    if(action.type!==DOWN_VOTE_COMMENT) {
        return state
    }
    const {id, voteScore} = action
    const comment = state.comments.byId[id]
    const allCommentIds = state.comments.allIds
    return {
        ...state,
        comments: {
            byId: {
                ...state.comments.byId,
                [id]: {
                    id: id,
                    timestamp: comment.timestamp,
                    body: comment.body,
                    author: comment.author,
                    parentId: comment.parentId,
                    voteScore: voteScore,
                    deleted: comment.deleted,
                    parentDeleted: comment.parentDeleted
                }
            },
            allIds: allCommentIds
        }
    }
}

export function addCategories(state=initialState, action) {
    if(action.type!==ADD_CATEGORIES) {
        return state
    }
    const categories = action.categories
    return {
        ...state,
        categories: categories
    }
}

// function getCommentsByPost(state=initialState, action) {
//     return state.posts.byId[action.id].commentIds.map(i => state.comments.byId[i]);
// }

// function getAllPosts(state=initialState, action) {
//     return state.posts.allIds.map(i => state.posts.byId[i]);
// }

// function getPostsByCategory(state=initialState, action) {
//     return state.posts.byCategory[action.category].map(i => state.posts.byId[i]);
// }
//
// function getPostById(state=initialState, action) {
//     return state.posts.byId[action.id];
// }
//
// function getAllCategories(state=initialState, action) {
//     return state.categories.map(c => c.name)
// }


function forum(state = initialState, action) {
    switch(action.type) {
        case ADD_POST :
            return addPost(state, action)
        case UPDATE_POST :
            return updatePost(state, action)
        case REMOVE_POST :
            return removePost(state, action)
        case UP_VOTE_POST :
            return upVotePost(state, action)
        case DOWN_VOTE_POST :
            return downVotePost(state, action)
        case ADD_COMMENT :
            return addComment(state, action)
        case UPDATE_COMMENT :
            return updateComment(state, action)
        case REMOVE_COMMENT :
            return removeComment(state, action)
        case UP_VOTE_COMMENT :
            return upVoteComment(state, action)
        case DOWN_VOTE_COMMENT :
            return downVoteComment(state, action)
        case ADD_CATEGORIES :
            return addCategories(state, action)
        default :
            return state
    }
}
export default combineReducers({
    forum
})<file_sep>/src/components/CommentCreateEdit.js
import React, { Component } from 'react';
import * as ReadableAPI from "../utils/ReadableAPI";
import {
    addCommentAction, updateCommentAction
} from "../actions";
import {connect} from "react-redux";
import {getId} from "../utils/ReadableAPI";

class CommentCreateEdit extends Component {
    state = {
        id: "",
        parentId: "",
        body: "",
        author: "",
    }

    constructor(props) {
        super(props)
        const {commentId, comments} = this.props
        if(commentId!=null) {
            this._body = comments.byId[commentId].body
            this._author = comments.byId[commentId].author
        }
        this.handleSaveClick = this.handleSaveClick.bind(this);
        this.handleCloseClick = this.handleCloseClick.bind(this);
    }

    handleSaveClick() {
        const body = this._body.value
        const author = this._author.value
        const {closeModal, actions} = this.props

        if(this.state.id===null) {
            const newId=getId()
            ReadableAPI.createComment(
                {
                    id: newId,
                    parentId: this.props.parentId,
                    body: body,
                    author: author,
                    timestamp: Date.now(),
                    voteScore: 0,
                    deleted: false,
                    parentDeleted: false
                }).then(
                    (comment) => {
                        this.setState({
                                id: newId,
                                parentId: this.props.parentId,
                                body: body,
                                author: author,
                                timestamp: comment.timestamp,
                                voteScore: comment.voteScore,
                                deleted: false,
                                parentDeleted: false
                            }
                        )
                        actions.addCommentProp({
                            id: newId,
                            parentId: this.state.parentId,
                            body: body,
                            author: author,
                            timestamp: comment.timestamp,
                            voteScore: comment.voteScore,
                            deleted: false,
                            parentDeleted: false
                        })
                        if(closeModal!=null) {
                            closeModal()
                        }
                    }
            )
        }
        else {
            ReadableAPI.updateComment(
                {
                    id: this.state.id,
                    author: author,
                    body: body,
                    timestamp: Date.now()
                }).then(
                    (comment) => {
                        this.setState({
                            id: this.state.id,
                            parentId: this.props.parentId,
                            body: body,
                            author: author,
                            timestamp: comment.timestamp
                            }
                        )
                        actions.updateCommentProp({
                            id: this.state.id,
                            parentId: this.props.parentId,
                            body: body,
                            author: author,
                            timestamp: comment.timestamp})
                        if(closeModal!=null) {
                            closeModal()
                        }
                    }
                )
        }
    }

    handleCloseClick() {
        const {closeModal} = this.props
        if (closeModal != null) {
            closeModal()
        }
    }

    componentDidMount() {
        const {closeModal, commentId, comments} = this.props
        this._body = this.state.body
        this._author = this.state.author
        this.setState({
            closeModal: closeModal==null?null:closeModal,
            id: commentId==null?null:commentId,
            body: commentId==null?"":comments.byId[commentId].body,
            author: commentId==null?"":comments.byId[commentId].author,
            parentId: commentId==null?"":comments.byId[commentId].parentId,
            timestamp: commentId==null?"":comments.byId[commentId].timestamp
        })
    }

    render() {
        return (

                                   <div className="form" id="commentCreateEdit">
                                       <fieldset title={this.state.id===null?"Create Comment":"Modify Comment"}>
                                       <label className="field-label">Body:</label><textarea rows={4} cols={80} className="field" type="text" minLength="255" maxLength="255"
                                                                                             ref={input => this._body = input} defaultValue={this._body}/><br/>
                                       {this.state.id === null ? (
                                           <div>
                                               <label className="field-label">Author:</label><input className="field" type="text" minLength="60" maxLength="60"
                                                                                                    ref={input => this._author = input} defaultValue={this._author}/><br/>
                                           </div>) : (
                                           <div>
                                               <label className="field-label">Author:</label><input readOnly={true} className="field" type="text" minLength="60" maxLength="60"  ref={input => this._author = input} defaultValue={this._author}/>
                                           </div>
                                       )
                                       }
                                       <button className="button" onClick={this.handleCloseClick}>Close</button>
                                       <button className="button" onClick={this.handleSaveClick}>Save</button>
                                       </fieldset>
                                   </div>
                               )

    }
}

function mapStateToProps ({ forum }) {
    return {
        categories: forum.categories,
        posts: forum.posts,
        comments: forum.comments
    }
}

function mapDispatchToProps (dispatch) {
    return {
        actions: {
            addCommentProp: (data) => dispatch(addCommentAction(data)),
            updateCommentProp: (data) => dispatch(updateCommentAction(data))
        }
    }
}

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(CommentCreateEdit)<file_sep>/src/components/PostDetails.js
import React, { Component } from 'react'
import CommentCreateEdit from "./CommentCreateEdit"
import PostCreateEdit from "./PostCreateEdit"
import {Link} from 'react-router-dom'
import * as ReadableAPI from "../utils/ReadableAPI"
import {
    addCategoriesAction, addCommentAction, addPostAction, downVoteCommentAction, downVotePostAction,
    removeCommentAction, removePostAction, updateCommentAction,
    updatePostAction,
    upVoteCommentAction,
    upVotePostAction
} from "../actions";
import {connect} from "react-redux";
import Modal from "react-modal";

Modal.setAppElement('#root');

class PostDetails extends Component {

    static contextTypes = {
         router: () => true // replace with PropTypes.object if you use them
    }

    state = {
        isCreateCommentModalOpen: false,
        isEditCommentModalOpen: [],
        isCreatePostModalOpen: false,
        isEditPostModalOpen: false,
    }

    openCreatePostModal() {
        this.setState(() => ({isCreatePostModalOpen: true}))
    }

    closeCreatePostModal(){
        this.setState(() => ({isCreatePostModalOpen: false}))
    }

    openEditPostModal(index) {
        this.setState(() => ({isEditPostModalOpen: true}))
    }

    closeEditPostModal(){
        this.setState(() => ({isEditPostModalOpen: false}))
    }

    handleDownVotePost = (postId, index) => {
        const {actions} = this.props
        ReadableAPI.downVotePost(postId).then(
            (post) => {
                actions.downVotePostProp({id: post.id, voteScore: post.voteScore})
            }
        )
    }

    handleUpVotePost = (postId, index) => {
        const {actions} = this.props
        ReadableAPI.upVotePost(postId).then(
            (post) => {
                actions.upVotePostProp({id: post.id, voteScore: post.voteScore})
            }
        )
    }

    handleDeletePost = (postId, index) => {
        const {actions} = this.props
        ReadableAPI.deletePost(postId).then(
            (post) => {
                actions.removePostProp(post.id)
                this.context.router.history.goBack()
            }
        )
    }

    componentDidMount() {
        const path = this.props.location.pathname
        const postId = path.substring(path.lastIndexOf('/')+1, path.length)
        const post = this.props.posts.byId[postId]
        const comments = this.props.posts.byId[postId].commentIds.map( i => this.props.comments.byId[i]).filter(c => (c.deleted===false && c.parentDeleted===false))
        this.setState(
            {
                post: post,
                comments: comments,
                isSortedByTimestamp: true,
                isSortedByVoteScore: false,
                isAscending: true
            })
    }

    openCreateCommentModal() {
        this.setState(() => ({isCreateCommentModalOpen: true}))
    }

    closeCreateCommentModal(){
        this.setState(() => ({isCreateCommentModalOpen: false}))
    }

    openEditCommentModal(index) {
        const isEditCommentModalOpen = this.state.isEditCommentModalOpen.map(f => false)
        isEditCommentModalOpen[index] = true
        this.setState(() => ({isEditCommentModalOpen: isEditCommentModalOpen}))
    }

    closeEditCommentModal(){
        const isEditCommentModalOpen = this.state.isEditCommentModalOpen.map(f => false)
        this.setState(() => ({isEditCommentModalOpen: isEditCommentModalOpen}))
    }

    handleDownVoteComment = (commentId, index) => {
        const {actions} = this.props
        ReadableAPI.downVoteComment(commentId).then(
            (comment) => {
                actions.downVoteCommentProp({id: comment.id, voteScore: comment.voteScore})
            }
        )
    }

    handleUpVoteComment = (commentId, index) => {
        const {actions} = this.props
        ReadableAPI.upVoteComment(commentId).then(
            (comment) => {
                actions.upVoteCommentProp({id: comment.id, voteScore: comment.voteScore})
            }
        )
    }

    handleDeleteComment = (commentId, index) => {
        const {actions} = this.props
        ReadableAPI.deleteComment(commentId).then(
            (comment) => {
                let commentsCloned=this.state.comments.slice()
                commentsCloned[index] = comment
                this.setState(
                    {
                        comments: commentsCloned
                    })
                actions.removeCommentProp(commentId)
            }
        )
    }

    handleSortByTimestamp = () => {
        let isAscending = true
        if(this.state.isSortedByTimestamp) {
            isAscending=!this.state.isAscending
        }
        this.setState({isSortedByTimestamp: true, isSortedByVoteScore: false, isAscending: isAscending})
    }

    handleSortByVoteScore = () => {
        let isAscending = true
        if(this.state.isSortedByVoteScore) {
            isAscending=!this.state.isAscending
        }
        this.setState({isSortedByTimestamp: false, isSortedByVoteScore: true, isAscending: isAscending})
    }

    constructor(props) {
        super(props)
        this.openCreateCommentModal = this.openCreateCommentModal.bind(this);
        this.closeCreateCommentModal = this.closeCreateCommentModal.bind(this);
        this.openEditCommentModal = this.openEditCommentModal.bind(this);
        this.closeEditCommentModal = this.closeEditCommentModal.bind(this);

        this.openCreatePostModal = this.openCreatePostModal.bind(this);
        this.closeCreatePostModal = this.closeCreatePostModal.bind(this);
        this.openEditPostModal = this.openEditPostModal.bind(this);
        this.closeEditPostModal = this.closeEditPostModal.bind(this);

        this.handleSortByTimestamp = this.handleSortByTimestamp.bind(this);
        this.handleSortByVoteScore = this.handleSortByVoteScore.bind(this);
    }

    render() {
        const path = this.props.location.pathname
        const postId = path.substring(path.lastIndexOf('/')+1, path.length)
        const post = this.props.posts.byId[postId]
        let {actions, categories, posts, comments} = this.props
        if(actions==null) {
            actions = {}
        }
        if(categories==null) {
            categories = []
        }
        if(posts==null) {
            posts = {allIds: [], byId: {}, byCategory: {}}
        }
        if(comments==null) {
            comments = {allIds: [], byId: {}}
        }
        const {isSortedByTimestamp, isSortedByVoteScore, isAscending} = this.state

        const handleDeleteComment = this.handleDeleteComment
        const isEditCommentModalOpen = this.state.isEditCommentModalOpen
        const closeEditCommentModal = this.closeEditCommentModal
        const openEditCommentModal = this.openEditCommentModal
        const handleDownVoteComment = this.handleDownVoteComment
        const handleUpVoteComment = this.handleUpVoteComment

        const handleDeletePost = this.handleDeletePost
        const isEditPostModalOpen = this.state.isEditPostModalOpen
        const closeEditPostModal = this.closeEditPostModal
        const openEditPostModal = this.openEditPostModal
        const handleDownVotePost = this.handleDownVotePost
        const handleUpVotePost = this.handleUpVotePost

        const postDateTime = new Date(post.timestamp)
        const postDateTimeStr=postDateTime.toLocaleString()
        return (
                    <div>
                        <h1>Post Details:</h1>
                        <div className="form" id="postDetails">
                            <table>
                                <tbody>
                                <tr className="table-row-even"><td className="table-column-label"><label className="field-label">Id:</label></td><td className="table-column-value"><label className="field-value">{post.id}</label></td></tr>
                                <tr className="table-row-odd"><td className="table-column-label"><label className="field-label">Timestamp:</label></td><td className="table-column-value"><label className="field-value">{postDateTimeStr}</label></td></tr>
                                <tr className="table-row-even"><td className="table-column-label"><label className="field-label">Title:</label></td><td className="table-column-value"><label className="field-value">{post.title}</label></td></tr>
                                <tr className="table-row-odd"><td className="table-column-label"><label className="field-label">Body:</label></td><td className="table-column-value"><label className="field-value">{post.body}</label></td></tr>
                                <tr className="table-row-even"><td className="table-column-label"><label className="field-label">Author:</label></td><td className="table-column-value"><label className="field-value">{post.author}</label></td></tr>
                                <tr className="table-row-odd"><td className="table-column-label"><label className="field-label">Category:</label></td><td className="table-column-value"><label className="field-value">{post.author}</label></td></tr>
                                <tr className="table-row-even"><td className="table-column-label"><label className="field-label">Vote Score:</label></td><td className="table-column-value"><label className="field-value">{post.voteScore}</label></td></tr>
                                <tr className="table-row-odd"><td className="table-column-label"><label className="field-label">Comment Count:</label></td><td className="table-column-value"><label className="field-value">{post.commentCount}</label></td></tr>
                                </tbody>
                            </table>
                            <table>
                                <tr>
                                    <td><button onClick={() => handleDeletePost(post.id)} name="Delete">Delete</button></td>
                                    <td><button onClick={() => handleDownVotePost(post.id)} name="Vote Down">Vote Down</button></td>
                                    <td><button onClick={() => handleUpVotePost(post.id)} name="Vote Up">Vote Up</button></td>
                                    <td>
                                        <button
                                            className='button'
                                            onClick={() => openEditPostModal()}>
                                            Edit
                                        </button>
                                        <div>
                                            <Modal
                                                className='modal'
                                                overlayClassName='overlay'
                                                isOpen={isEditPostModalOpen}
                                                onRequestClose={closeEditPostModal}
                                                contentLabel='Edit Post'
                                                backdropColor = {'white'}
                                                backdropOpacity = {1}
                                                animationIn={'slideInLeft'}
                                                animationOut={'slideOutRight'}
                                            >
                                                {isEditPostModalOpen && <PostCreateEdit closeModal={closeEditPostModal} postId={post.id}/>}
                                            </Modal>
                                        </div>
                                    </td>
                                </tr>
                            </table>
                        </div>
                        <div id="commentsForPost">
                            <h1>Comments:</h1>
                            <table>
                                <thead className="table-header">
                                    <tr>
                                        <th>Id</th>
                                        <th>Timestamp<button><img height='20' width='20' src={(isSortedByTimestamp?(isAscending?'../sort-up.svg':'../sort-down.svg'):'../sort-arrows-couple-pointing-up-and-down.svg')} alt="sort by timestamp" onClick={this.handleSortByTimestamp} /></button></th>
                                        <th>Body</th>
                                        <th>Author</th>
                                        <th>Vote Score<button><img height='20' width='20' src={(isSortedByVoteScore?(isAscending?'../sort-up.svg':'../sort-down.svg'):'../sort-arrows-couple-pointing-up-and-down.svg')} alt="sort by vote score" onClick={this.handleSortByVoteScore} /></button></th>
                                        <th>Down Vote</th>
                                        <th>Up Vote</th>
                                        <th>Delete</th>
                                        <th>Edit</th>
                                    </tr>
                                </thead>
                                <tbody>
                                {(comments===null || comments.allIds==null || comments.allIds.length===0 || post===null || post.commentCount===0) ?
                                    <tr><td colSpan={12}>no comments available</td></tr>
                                    :comments.allIds.map(commentId => comments.byId[commentId] )
                                        .filter(comment => comment.deleted===false)
                                        .filter(comment => comment.parentId===postId)
                                        .sort(function(a,b) {
                                            if(isSortedByTimestamp) {
                                                if(isAscending) {
                                                    return a.timestamp - b.timestamp
                                                }
                                                else {
                                                    return b.timestamp - a.timestamp
                                                }
                                            }
                                            else if(isSortedByVoteScore) {
                                                if(isAscending) {
                                                    return a.voteScore - b.voteScore
                                                }
                                                else {
                                                    return b.voteScore - a.voteScore
                                                }
                                            }
                                            return 0
                                        })
                                        .map(function (comment, index) {
                                    const commentDateTime = new Date(comment.timestamp)
                                    const commentDateTimeStr=commentDateTime.toLocaleString()
                                    return (
                                        <tr className={index%2===0?"table-row-even":"table-row-odd"} key={comment.id}>
                                            <td>{comment.id}</td>
                                            <td>{commentDateTimeStr}</td>
                                            <td>{comment.body}</td>
                                            <td>{comment.author}</td>
                                            <td>{comment.voteScore}</td>
                                            <td><button onClick={() => handleDownVoteComment(comment.id, index)} name="Vote Down">Vote Down</button></td>
                                            <td><button onClick={() => handleUpVoteComment(comment.id, index)} name="Vote Up">Vote Up</button></td>
                                            <td><button onClick={() => handleDeleteComment(comment.id, index)} name="Delete">Delete</button></td>
                                            <td>
                                                <button
                                                    className='button'
                                                    onClick={() => openEditCommentModal(index)}>
                                                    Edit
                                                </button>
                                                <div>
                                                    <Modal
                                                        className='modal'
                                                        overlayClassName='overlay'
                                                        isOpen={isEditCommentModalOpen[index]}
                                                        onRequestClose={closeEditCommentModal}
                                                        contentLabel='Edit Comment'
                                                        backdropColor = {'white'}
                                                        backdropOpacity = {1}
                                                        animationIn={'slideInLeft'}
                                                        animationOut={'slideOutRight'}
                                                    >
                                                        {isEditCommentModalOpen[index] && <CommentCreateEdit closeModal={closeEditCommentModal} commentId={comment.id}/>}
                                                    </Modal>
                                                </div>
                                            </td>
                                        </tr>
                                        )})}
                                </tbody>
                            </table>
                        </div>
                        <div id="addComment">
                            <table>
                                <tbody>
                                <tr>
                                    <td>
                                        <button
                                            className="button"
                                            onClick={this.context.router.history.goBack}>
                                            Back
                                        </button>
                                    </td>
                                    <td>
                                        <Link to={{
                                            pathname: '/'
                                        }}>
                                            Home
                                        </Link>
                                    </td>
                                    <td>
                                        <button
                                            className='button'
                                            onClick={() => this.openCreateCommentModal()}>
                                            Create
                                        </button>
                                        <div>
                                            <Modal
                                                className='modal'
                                                overlayClassName='overlay'
                                                isOpen={this.state.isCreateCommentModalOpen}
                                                onRequestClose={this.closeCreateCommentModal}
                                                contentLabel='Create Comment'
                                                backdropColor = {'white'}
                                                backdropOpacity = {1}
                                                animationIn={'slideInLeft'}
                                                animationOut={'slideOutRight'}
                                            >
                                                {this.state.isCreateCommentModalOpen && <CommentCreateEdit closeModal={this.closeCreateCommentModal} parentId={post.id}/>}
                                            </Modal>
                                        </div>
                                    </td>
                                </tr>
                                </tbody>
                            </table>
                        </div>
                    </div>
        );
    }
}

function mapStateToProps ({ forum }) {
    return {
        categories: forum.categories,
        posts: forum.posts,
        comments: forum.comments
    }
}

function mapDispatchToProps (dispatch) {
    return {
        actions: {
            addPostProp: (data) => dispatch(addPostAction(data)),
            removePostProp: (data) => dispatch(removePostAction(data)),
            updatePostProp: (data) => dispatch(updatePostAction(data)),
            upVotePostProp: (data) => dispatch(upVotePostAction(data)),
            downVotePostProp: (data) => dispatch(downVotePostAction(data)),
            addCommentProp: (data) => dispatch(addCommentAction(data)),
            removeCommentProp: (data) => dispatch(removeCommentAction(data)),
            updateCommentProp: (data) => dispatch(updateCommentAction(data)),
            upVoteCommentProp: (data) => dispatch(upVoteCommentAction(data)),
            downVoteCommentProp: (data) => dispatch(downVoteCommentAction(data)),
            addCategoriesProp: (data) => dispatch(addCategoriesAction(data))
        }
    }
}

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(PostDetails)