[
  {
    "path": ".gitignore",
    "content": "# See http://help.github.com/ignore-files/ for more about ignoring files.\n\n# dependencies\nnode_modules\n\n# testing\ncoverage\n\n# production\nbuild\n\n# misc\n.DS_Store\n.env\nnpm-debug.log\n.idea"
  },
  {
    "path": "LICENSE.md",
    "content": "MIT License\n\nCopyright (c) 2020 GoThinkster\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# ![React + Redux Example App](project-logo.png)\n\n[![RealWorld Frontend](https://img.shields.io/badge/realworld-frontend-%23783578.svg)](http://realworld.io)\n\n> ### React + Redux codebase containing real world examples (CRUD, auth, advanced patterns, etc) that adheres to the [RealWorld](https://github.com/gothinkster/realworld-example-apps) spec and API.\n\n<a href=\"https://stackblitz.com/edit/react-redux-realworld\" target=\"_blank\"><img width=\"187\" src=\"https://github.com/gothinkster/realworld/blob/master/media/edit_on_blitz.png?raw=true\" /></a>&nbsp;&nbsp;<a href=\"https://thinkster.io/tutorials/build-a-real-world-react-redux-application\" target=\"_blank\"><img width=\"384\" src=\"https://raw.githubusercontent.com/gothinkster/realworld/master/media/learn-btn-hr.png\" /></a>\n\n### [Demo](https://react-redux.realworld.io)&nbsp;&nbsp;&nbsp;&nbsp;[RealWorld](https://github.com/gothinkster/realworld)\n\nOriginally created for this [GH issue](https://github.com/reactjs/redux/issues/1353). The codebase is now feature complete; please submit bug fixes via pull requests & feedback via issues.\n\nWe also have notes in [**our wiki**](https://github.com/gothinkster/react-redux-realworld-example-app/wiki) about how the various patterns used in this codebase and how they work (thanks [@thejmazz](https://github.com/thejmazz)!)\n\n\n## Getting started\n\nYou can view a live demo over at https://react-redux.realworld.io/\n\nTo get the frontend running locally:\n\n- Clone this repo\n- `npm install` to install all req'd dependencies\n- `npm start` to start the local server (this project uses create-react-app)\n\nLocal web server will use port 4100 instead of standard React's port 3000 to prevent conflicts with some backends like Node or Rails. You can configure port in scripts section of `package.json`: we use [cross-env](https://github.com/kentcdodds/cross-env) to set environment variable PORT for React scripts, this is Windows-compatible way of setting environment variables.\n \nAlternatively, you can add `.env` file in the root folder of project to set environment variables (use PORT to change webserver's port). This file will be ignored by git, so it is suitable for API keys and other sensitive stuff. Refer to [dotenv](https://github.com/motdotla/dotenv) and [React](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-development-environment-variables-in-env) documentation for more details. Also, please remove setting variable via script section of `package.json` - `dotenv` never override variables if they are already set.  \n\n### Making requests to the backend API\n\nFor convenience, we have a live API server running at https://conduit.productionready.io/api for the application to make requests against. You can view [the API spec here](https://github.com/GoThinkster/productionready/blob/master/api) which contains all routes & responses for the server.\n\nThe source code for the backend server (available for Node, Rails and Django) can be found in the [main RealWorld repo](https://github.com/gothinkster/realworld).\n\nIf you want to change the API URL to a local server, simply edit `src/agent.js` and change `API_ROOT` to the local server's URL (i.e. `http://localhost:3000/api`)\n\n\n## Functionality overview\n\nThe example application is a social blogging site (i.e. a Medium.com clone) called \"Conduit\". It uses a custom API for all requests, including authentication. You can view a live demo over at https://redux.productionready.io/\n\n**General functionality:**\n\n- Authenticate users via JWT (login/signup pages + logout button on settings page)\n- CRU* users (sign up & settings page - no deleting required)\n- CRUD Articles\n- CR*D Comments on articles (no updating required)\n- GET and display paginated lists of articles\n- Favorite articles\n- Follow other users\n\n**The general page breakdown looks like this:**\n\n- Home page (URL: /#/ )\n    - List of tags\n    - List of articles pulled from either Feed, Global, or by Tag\n    - Pagination for list of articles\n- Sign in/Sign up pages (URL: /#/login, /#/register )\n    - Use JWT (store the token in localStorage)\n- Settings page (URL: /#/settings )\n- Editor page to create/edit articles (URL: /#/editor, /#/editor/article-slug-here )\n- Article page (URL: /#/article/article-slug-here )\n    - Delete article button (only shown to article's author)\n    - Render markdown from server client side\n    - Comments section at bottom of page\n    - Delete comment button (only shown to comment's author)\n- Profile page (URL: /#/@username, /#/@username/favorites )\n    - Show basic user info\n    - List of articles populated from author's created articles or author's favorited articles\n\n<br />\n\n[![Brought to you by Thinkster](https://raw.githubusercontent.com/gothinkster/realworld/master/media/end.png)](https://thinkster.io)\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react-redux-realworld-example-app\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"devDependencies\": {\n    \"cross-env\": \"^5.1.4\",\n    \"react-scripts\": \"1.1.1\"\n  },\n  \"dependencies\": {\n    \"history\": \"^4.6.3\",\n    \"marked\": \"^0.3.6\",\n    \"prop-types\": \"^15.5.10\",\n    \"react\": \"^16.3.0\",\n    \"react-dom\": \"^16.3.0\",\n    \"react-redux\": \"^5.0.7\",\n    \"react-router\": \"^4.1.2\",\n    \"react-router-dom\": \"^4.1.2\",\n    \"react-router-redux\": \"^5.0.0-alpha.6\",\n    \"redux\": \"^3.6.0\",\n    \"redux-devtools-extension\": \"^2.13.2\",\n    \"redux-logger\": \"^3.0.1\",\n    \"superagent\": \"^3.8.2\",\n    \"superagent-promise\": \"^1.1.0\"\n  },\n  \"scripts\": {\n    \"start\": \"cross-env PORT=4100 react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"cross-env PORT=4100 react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  }\n}\n"
  },
  {
    "path": "public/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    <link rel=\"shortcut icon\" href=\"%PUBLIC_URL%/favicon.ico\">\n    <link rel=\"stylesheet\" href=\"//demo.productionready.io/main.css\">\n    <link href=\"//code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css\" rel=\"stylesheet\" type=\"text/css\">\n    <link href=\"//fonts.googleapis.com/css?family=Titillium+Web:700|Source+Serif+Pro:400,700|Merriweather+Sans:400,700|Source+Sans+Pro:400,300,600,700,300italic,400italic,600italic,700italic\" rel=\"stylesheet\" type=\"text/css\">\n    <!--\n      Notice the use of %PUBLIC_URL% in the tag above.\n      It will be replaced with the URL of the `public` folder during the build.\n      Only files inside the `public` folder can be referenced from the HTML.\n\n      Unlike \"/favicon.ico\" or \"favico.ico\", \"%PUBLIC_URL%/favicon.ico\" will\n      work correctly both with client-side routing and a non-root public URL.\n      Learn how to configure a non-root public URL by running `npm run build`.\n    -->\n    <title>Conduit</title>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <!--\n      This HTML file is a template.\n      If you open it directly in the browser, you will see an empty page.\n\n      You can add webfonts, meta tags, or analytics to this file.\n      The build step will place the bundled scripts into the <body> tag.\n\n      To begin the development, run `npm start`.\n      To create a production bundle, use `npm run build`.\n    -->\n  </body>\n</html>\n"
  },
  {
    "path": "src/agent.js",
    "content": "import superagentPromise from 'superagent-promise';\nimport _superagent from 'superagent';\n\nconst superagent = superagentPromise(_superagent, global.Promise);\n\nconst API_ROOT = 'https://conduit.productionready.io/api';\n\nconst encode = encodeURIComponent;\nconst responseBody = res => res.body;\n\nlet token = null;\nconst tokenPlugin = req => {\n  if (token) {\n    req.set('authorization', `Token ${token}`);\n  }\n}\n\nconst requests = {\n  del: url =>\n    superagent.del(`${API_ROOT}${url}`).use(tokenPlugin).then(responseBody),\n  get: url =>\n    superagent.get(`${API_ROOT}${url}`).use(tokenPlugin).then(responseBody),\n  put: (url, body) =>\n    superagent.put(`${API_ROOT}${url}`, body).use(tokenPlugin).then(responseBody),\n  post: (url, body) =>\n    superagent.post(`${API_ROOT}${url}`, body).use(tokenPlugin).then(responseBody)\n};\n\nconst Auth = {\n  current: () =>\n    requests.get('/user'),\n  login: (email, password) =>\n    requests.post('/users/login', { user: { email, password } }),\n  register: (username, email, password) =>\n    requests.post('/users', { user: { username, email, password } }),\n  save: user =>\n    requests.put('/user', { user })\n};\n\nconst Tags = {\n  getAll: () => requests.get('/tags')\n};\n\nconst limit = (count, p) => `limit=${count}&offset=${p ? p * count : 0}`;\nconst omitSlug = article => Object.assign({}, article, { slug: undefined })\nconst Articles = {\n  all: page =>\n    requests.get(`/articles?${limit(10, page)}`),\n  byAuthor: (author, page) =>\n    requests.get(`/articles?author=${encode(author)}&${limit(5, page)}`),\n  byTag: (tag, page) =>\n    requests.get(`/articles?tag=${encode(tag)}&${limit(10, page)}`),\n  del: slug =>\n    requests.del(`/articles/${slug}`),\n  favorite: slug =>\n    requests.post(`/articles/${slug}/favorite`),\n  favoritedBy: (author, page) =>\n    requests.get(`/articles?favorited=${encode(author)}&${limit(5, page)}`),\n  feed: () =>\n    requests.get('/articles/feed?limit=10&offset=0'),\n  get: slug =>\n    requests.get(`/articles/${slug}`),\n  unfavorite: slug =>\n    requests.del(`/articles/${slug}/favorite`),\n  update: article =>\n    requests.put(`/articles/${article.slug}`, { article: omitSlug(article) }),\n  create: article =>\n    requests.post('/articles', { article })\n};\n\nconst Comments = {\n  create: (slug, comment) =>\n    requests.post(`/articles/${slug}/comments`, { comment }),\n  delete: (slug, commentId) =>\n    requests.del(`/articles/${slug}/comments/${commentId}`),\n  forArticle: slug =>\n    requests.get(`/articles/${slug}/comments`)\n};\n\nconst Profile = {\n  follow: username =>\n    requests.post(`/profiles/${username}/follow`),\n  get: username =>\n    requests.get(`/profiles/${username}`),\n  unfollow: username =>\n    requests.del(`/profiles/${username}/follow`)\n};\n\nexport default {\n  Articles,\n  Auth,\n  Comments,\n  Profile,\n  Tags,\n  setToken: _token => { token = _token; }\n};\n"
  },
  {
    "path": "src/components/App.js",
    "content": "import agent from '../agent';\nimport Header from './Header';\nimport React from 'react';\nimport { connect } from 'react-redux';\nimport { APP_LOAD, REDIRECT } from '../constants/actionTypes';\nimport { Route, Switch } from 'react-router-dom';\nimport Article from '../components/Article';\nimport Editor from '../components/Editor';\nimport Home from '../components/Home';\nimport Login from '../components/Login';\nimport Profile from '../components/Profile';\nimport ProfileFavorites from '../components/ProfileFavorites';\nimport Register from '../components/Register';\nimport Settings from '../components/Settings';\nimport { store } from '../store';\nimport { push } from 'react-router-redux';\n\nconst mapStateToProps = state => {\n  return {\n    appLoaded: state.common.appLoaded,\n    appName: state.common.appName,\n    currentUser: state.common.currentUser,\n    redirectTo: state.common.redirectTo\n  }};\n\nconst mapDispatchToProps = dispatch => ({\n  onLoad: (payload, token) =>\n    dispatch({ type: APP_LOAD, payload, token, skipTracking: true }),\n  onRedirect: () =>\n    dispatch({ type: REDIRECT })\n});\n\nclass App extends React.Component {\n  componentWillReceiveProps(nextProps) {\n    if (nextProps.redirectTo) {\n      // this.context.router.replace(nextProps.redirectTo);\n      store.dispatch(push(nextProps.redirectTo));\n      this.props.onRedirect();\n    }\n  }\n\n  componentWillMount() {\n    const token = window.localStorage.getItem('jwt');\n    if (token) {\n      agent.setToken(token);\n    }\n\n    this.props.onLoad(token ? agent.Auth.current() : null, token);\n  }\n\n  render() {\n    if (this.props.appLoaded) {\n      return (\n        <div>\n          <Header\n            appName={this.props.appName}\n            currentUser={this.props.currentUser} />\n            <Switch>\n            <Route exact path=\"/\" component={Home}/>\n            <Route path=\"/login\" component={Login} />\n            <Route path=\"/register\" component={Register} />\n            <Route path=\"/editor/:slug\" component={Editor} />\n            <Route path=\"/editor\" component={Editor} />\n            <Route path=\"/article/:id\" component={Article} />\n            <Route path=\"/settings\" component={Settings} />\n            <Route path=\"/@:username/favorites\" component={ProfileFavorites} />\n            <Route path=\"/@:username\" component={Profile} />\n            </Switch>\n        </div>\n      );\n    }\n    return (\n      <div>\n        <Header\n          appName={this.props.appName}\n          currentUser={this.props.currentUser} />\n      </div>\n    );\n  }\n}\n\n// App.contextTypes = {\n//   router: PropTypes.object.isRequired\n// };\n\nexport default connect(mapStateToProps, mapDispatchToProps)(App);\n"
  },
  {
    "path": "src/components/Article/ArticleActions.js",
    "content": "import { Link } from 'react-router-dom';\nimport React from 'react';\nimport agent from '../../agent';\nimport { connect } from 'react-redux';\nimport { DELETE_ARTICLE } from '../../constants/actionTypes';\n\nconst mapDispatchToProps = dispatch => ({\n  onClickDelete: payload =>\n    dispatch({ type: DELETE_ARTICLE, payload })\n});\n\nconst ArticleActions = props => {\n  const article = props.article;\n  const del = () => {\n    props.onClickDelete(agent.Articles.del(article.slug))\n  };\n  if (props.canModify) {\n    return (\n      <span>\n\n        <Link\n          to={`/editor/${article.slug}`}\n          className=\"btn btn-outline-secondary btn-sm\">\n          <i className=\"ion-edit\"></i> Edit Article\n        </Link>\n\n        <button className=\"btn btn-outline-danger btn-sm\" onClick={del}>\n          <i className=\"ion-trash-a\"></i> Delete Article\n        </button>\n\n      </span>\n    );\n  }\n\n  return (\n    <span>\n    </span>\n  );\n};\n\nexport default connect(() => ({}), mapDispatchToProps)(ArticleActions);\n"
  },
  {
    "path": "src/components/Article/ArticleMeta.js",
    "content": "import ArticleActions from './ArticleActions';\nimport { Link } from 'react-router-dom';\nimport React from 'react';\n\nconst ArticleMeta = props => {\n  const article = props.article;\n  return (\n    <div className=\"article-meta\">\n      <Link to={`/@${article.author.username}`}>\n        <img src={article.author.image} alt={article.author.username} />\n      </Link>\n\n      <div className=\"info\">\n        <Link to={`/@${article.author.username}`} className=\"author\">\n          {article.author.username}\n        </Link>\n        <span className=\"date\">\n          {new Date(article.createdAt).toDateString()}\n        </span>\n      </div>\n\n      <ArticleActions canModify={props.canModify} article={article} />\n    </div>\n  );\n};\n\nexport default ArticleMeta;\n"
  },
  {
    "path": "src/components/Article/Comment.js",
    "content": "import DeleteButton from './DeleteButton';\nimport { Link } from 'react-router-dom';\nimport React from 'react';\n\nconst Comment = props => {\n  const comment = props.comment;\n  const show = props.currentUser &&\n    props.currentUser.username === comment.author.username;\n  return (\n    <div className=\"card\">\n      <div className=\"card-block\">\n        <p className=\"card-text\">{comment.body}</p>\n      </div>\n      <div className=\"card-footer\">\n        <Link\n          to={`/@${comment.author.username}`}\n          className=\"comment-author\">\n          <img src={comment.author.image} className=\"comment-author-img\" alt={comment.author.username} />\n        </Link>\n        &nbsp;\n        <Link\n          to={`/@${comment.author.username}`}\n          className=\"comment-author\">\n          {comment.author.username}\n        </Link>\n        <span className=\"date-posted\">\n          {new Date(comment.createdAt).toDateString()}\n        </span>\n        <DeleteButton show={show} slug={props.slug} commentId={comment.id} />\n      </div>\n    </div>\n  );\n};\n\nexport default Comment;\n"
  },
  {
    "path": "src/components/Article/CommentContainer.js",
    "content": "import CommentInput from './CommentInput';\nimport CommentList from './CommentList';\nimport { Link } from 'react-router-dom';\nimport React from 'react';\n\nconst CommentContainer = props => {\n  if (props.currentUser) {\n    return (\n      <div className=\"col-xs-12 col-md-8 offset-md-2\">\n        <div>\n          <list-errors errors={props.errors}></list-errors>\n          <CommentInput slug={props.slug} currentUser={props.currentUser} />\n        </div>\n\n        <CommentList\n          comments={props.comments}\n          slug={props.slug}\n          currentUser={props.currentUser} />\n      </div>\n    );\n  } else {\n    return (\n      <div className=\"col-xs-12 col-md-8 offset-md-2\">\n        <p>\n          <Link to=\"/login\">Sign in</Link>\n          &nbsp;or&nbsp;\n          <Link to=\"/register\">sign up</Link>\n          &nbsp;to add comments on this article.\n        </p>\n\n        <CommentList\n          comments={props.comments}\n          slug={props.slug}\n          currentUser={props.currentUser} />\n      </div>\n    );\n  }\n};\n\nexport default CommentContainer;\n"
  },
  {
    "path": "src/components/Article/CommentInput.js",
    "content": "import React from 'react';\nimport agent from '../../agent';\nimport { connect } from 'react-redux';\nimport { ADD_COMMENT } from '../../constants/actionTypes';\n\nconst mapDispatchToProps = dispatch => ({\n  onSubmit: payload =>\n    dispatch({ type: ADD_COMMENT, payload })\n});\n\nclass CommentInput extends React.Component {\n  constructor() {\n    super();\n    this.state = {\n      body: ''\n    };\n\n    this.setBody = ev => {\n      this.setState({ body: ev.target.value });\n    };\n\n    this.createComment = ev => {\n      ev.preventDefault();\n      const payload = agent.Comments.create(this.props.slug,\n        { body: this.state.body });\n      this.setState({ body: '' });\n      this.props.onSubmit(payload);\n    };\n  }\n\n  render() {\n    return (\n      <form className=\"card comment-form\" onSubmit={this.createComment}>\n        <div className=\"card-block\">\n          <textarea className=\"form-control\"\n            placeholder=\"Write a comment...\"\n            value={this.state.body}\n            onChange={this.setBody}\n            rows=\"3\">\n          </textarea>\n        </div>\n        <div className=\"card-footer\">\n          <img\n            src={this.props.currentUser.image}\n            className=\"comment-author-img\"\n            alt={this.props.currentUser.username} />\n          <button\n            className=\"btn btn-sm btn-primary\"\n            type=\"submit\">\n            Post Comment\n          </button>\n        </div>\n      </form>\n    );\n  }\n}\n\nexport default connect(() => ({}), mapDispatchToProps)(CommentInput);\n"
  },
  {
    "path": "src/components/Article/CommentList.js",
    "content": "import Comment from './Comment';\nimport React from 'react';\n\nconst CommentList = props => {\n  return (\n    <div>\n      {\n        props.comments.map(comment => {\n          return (\n            <Comment\n              comment={comment}\n              currentUser={props.currentUser}\n              slug={props.slug}\n              key={comment.id} />\n          );\n        })\n      }\n    </div>\n  );\n};\n\nexport default CommentList;\n"
  },
  {
    "path": "src/components/Article/DeleteButton.js",
    "content": "import React from 'react';\nimport agent from '../../agent';\nimport { connect } from 'react-redux';\nimport { DELETE_COMMENT } from '../../constants/actionTypes';\n\nconst mapDispatchToProps = dispatch => ({\n  onClick: (payload, commentId) =>\n    dispatch({ type: DELETE_COMMENT, payload, commentId })\n});\n\nconst DeleteButton = props => {\n  const del = () => {\n    const payload = agent.Comments.delete(props.slug, props.commentId);\n    props.onClick(payload, props.commentId);\n  };\n\n  if (props.show) {\n    return (\n      <span className=\"mod-options\">\n        <i className=\"ion-trash-a\" onClick={del}></i>\n      </span>\n    );\n  }\n  return null;\n};\n\nexport default connect(() => ({}), mapDispatchToProps)(DeleteButton);\n"
  },
  {
    "path": "src/components/Article/index.js",
    "content": "import ArticleMeta from './ArticleMeta';\nimport CommentContainer from './CommentContainer';\nimport React from 'react';\nimport agent from '../../agent';\nimport { connect } from 'react-redux';\nimport marked from 'marked';\nimport { ARTICLE_PAGE_LOADED, ARTICLE_PAGE_UNLOADED } from '../../constants/actionTypes';\n\nconst mapStateToProps = state => ({\n  ...state.article,\n  currentUser: state.common.currentUser\n});\n\nconst mapDispatchToProps = dispatch => ({\n  onLoad: payload =>\n    dispatch({ type: ARTICLE_PAGE_LOADED, payload }),\n  onUnload: () =>\n    dispatch({ type: ARTICLE_PAGE_UNLOADED })\n});\n\nclass Article extends React.Component {\n  componentWillMount() {\n    this.props.onLoad(Promise.all([\n      agent.Articles.get(this.props.match.params.id),\n      agent.Comments.forArticle(this.props.match.params.id)\n    ]));\n  }\n\n  componentWillUnmount() {\n    this.props.onUnload();\n  }\n\n  render() {\n    if (!this.props.article) {\n      return null;\n    }\n\n    const markup = { __html: marked(this.props.article.body, { sanitize: true }) };\n    const canModify = this.props.currentUser &&\n      this.props.currentUser.username === this.props.article.author.username;\n    return (\n      <div className=\"article-page\">\n\n        <div className=\"banner\">\n          <div className=\"container\">\n\n            <h1>{this.props.article.title}</h1>\n            <ArticleMeta\n              article={this.props.article}\n              canModify={canModify} />\n\n          </div>\n        </div>\n\n        <div className=\"container page\">\n\n          <div className=\"row article-content\">\n            <div className=\"col-xs-12\">\n\n              <div dangerouslySetInnerHTML={markup}></div>\n\n              <ul className=\"tag-list\">\n                {\n                  this.props.article.tagList.map(tag => {\n                    return (\n                      <li\n                        className=\"tag-default tag-pill tag-outline\"\n                        key={tag}>\n                        {tag}\n                      </li>\n                    );\n                  })\n                }\n              </ul>\n\n            </div>\n          </div>\n\n          <hr />\n\n          <div className=\"article-actions\">\n          </div>\n\n          <div className=\"row\">\n            <CommentContainer\n              comments={this.props.comments || []}\n              errors={this.props.commentErrors}\n              slug={this.props.match.params.id}\n              currentUser={this.props.currentUser} />\n          </div>\n        </div>\n      </div>\n    );\n  }\n}\n\nexport default connect(mapStateToProps, mapDispatchToProps)(Article);\n"
  },
  {
    "path": "src/components/ArticleList.js",
    "content": "import ArticlePreview from './ArticlePreview';\nimport ListPagination from './ListPagination';\nimport React from 'react';\n\nconst ArticleList = props => {\n  if (!props.articles) {\n    return (\n      <div className=\"article-preview\">Loading...</div>\n    );\n  }\n\n  if (props.articles.length === 0) {\n    return (\n      <div className=\"article-preview\">\n        No articles are here... yet.\n      </div>\n    );\n  }\n\n  return (\n    <div>\n      {\n        props.articles.map(article => {\n          return (\n            <ArticlePreview article={article} key={article.slug} />\n          );\n        })\n      }\n\n      <ListPagination\n        pager={props.pager}\n        articlesCount={props.articlesCount}\n        currentPage={props.currentPage} />\n    </div>\n  );\n};\n\nexport default ArticleList;\n"
  },
  {
    "path": "src/components/ArticlePreview.js",
    "content": "import React from 'react';\nimport { Link } from 'react-router-dom';\nimport agent from '../agent';\nimport { connect } from 'react-redux';\nimport { ARTICLE_FAVORITED, ARTICLE_UNFAVORITED } from '../constants/actionTypes';\n\nconst FAVORITED_CLASS = 'btn btn-sm btn-primary';\nconst NOT_FAVORITED_CLASS = 'btn btn-sm btn-outline-primary';\n\nconst mapDispatchToProps = dispatch => ({\n  favorite: slug => dispatch({\n    type: ARTICLE_FAVORITED,\n    payload: agent.Articles.favorite(slug)\n  }),\n  unfavorite: slug => dispatch({\n    type: ARTICLE_UNFAVORITED,\n    payload: agent.Articles.unfavorite(slug)\n  })\n});\n\nconst ArticlePreview = props => {\n  const article = props.article;\n  const favoriteButtonClass = article.favorited ?\n    FAVORITED_CLASS :\n    NOT_FAVORITED_CLASS;\n\n  const handleClick = ev => {\n    ev.preventDefault();\n    if (article.favorited) {\n      props.unfavorite(article.slug);\n    } else {\n      props.favorite(article.slug);\n    }\n  };\n\n  return (\n    <div className=\"article-preview\">\n      <div className=\"article-meta\">\n        <Link to={`/@${article.author.username}`}>\n          <img src={article.author.image} alt={article.author.username} />\n        </Link>\n\n        <div className=\"info\">\n          <Link className=\"author\" to={`/@${article.author.username}`}>\n            {article.author.username}\n          </Link>\n          <span className=\"date\">\n            {new Date(article.createdAt).toDateString()}\n          </span>\n        </div>\n\n        <div className=\"pull-xs-right\">\n          <button className={favoriteButtonClass} onClick={handleClick}>\n            <i className=\"ion-heart\"></i> {article.favoritesCount}\n          </button>\n        </div>\n      </div>\n\n      <Link to={`/article/${article.slug}`} className=\"preview-link\">\n        <h1>{article.title}</h1>\n        <p>{article.description}</p>\n        <span>Read more...</span>\n        <ul className=\"tag-list\">\n          {\n            article.tagList.map(tag => {\n              return (\n                <li className=\"tag-default tag-pill tag-outline\" key={tag}>\n                  {tag}\n                </li>\n              )\n            })\n          }\n        </ul>\n      </Link>\n    </div>\n  );\n}\n\nexport default connect(() => ({}), mapDispatchToProps)(ArticlePreview);\n"
  },
  {
    "path": "src/components/Editor.js",
    "content": "import ListErrors from './ListErrors';\nimport React from 'react';\nimport agent from '../agent';\nimport { connect } from 'react-redux';\nimport {\n  ADD_TAG,\n  EDITOR_PAGE_LOADED,\n  REMOVE_TAG,\n  ARTICLE_SUBMITTED,\n  EDITOR_PAGE_UNLOADED,\n  UPDATE_FIELD_EDITOR\n} from '../constants/actionTypes';\n\nconst mapStateToProps = state => ({\n  ...state.editor\n});\n\nconst mapDispatchToProps = dispatch => ({\n  onAddTag: () =>\n    dispatch({ type: ADD_TAG }),\n  onLoad: payload =>\n    dispatch({ type: EDITOR_PAGE_LOADED, payload }),\n  onRemoveTag: tag =>\n    dispatch({ type: REMOVE_TAG, tag }),\n  onSubmit: payload =>\n    dispatch({ type: ARTICLE_SUBMITTED, payload }),\n  onUnload: payload =>\n    dispatch({ type: EDITOR_PAGE_UNLOADED }),\n  onUpdateField: (key, value) =>\n    dispatch({ type: UPDATE_FIELD_EDITOR, key, value })\n});\n\nclass Editor extends React.Component {\n  constructor() {\n    super();\n\n    const updateFieldEvent =\n      key => ev => this.props.onUpdateField(key, ev.target.value);\n    this.changeTitle = updateFieldEvent('title');\n    this.changeDescription = updateFieldEvent('description');\n    this.changeBody = updateFieldEvent('body');\n    this.changeTagInput = updateFieldEvent('tagInput');\n\n    this.watchForEnter = ev => {\n      if (ev.keyCode === 13) {\n        ev.preventDefault();\n        this.props.onAddTag();\n      }\n    };\n\n    this.removeTagHandler = tag => () => {\n      this.props.onRemoveTag(tag);\n    };\n\n    this.submitForm = ev => {\n      ev.preventDefault();\n      const article = {\n        title: this.props.title,\n        description: this.props.description,\n        body: this.props.body,\n        tagList: this.props.tagList\n      };\n\n      const slug = { slug: this.props.articleSlug };\n      const promise = this.props.articleSlug ?\n        agent.Articles.update(Object.assign(article, slug)) :\n        agent.Articles.create(article);\n\n      this.props.onSubmit(promise);\n    };\n  }\n\n  componentWillReceiveProps(nextProps) {\n    if (this.props.match.params.slug !== nextProps.match.params.slug) {\n      if (nextProps.match.params.slug) {\n        this.props.onUnload();\n        return this.props.onLoad(agent.Articles.get(this.props.match.params.slug));\n      }\n      this.props.onLoad(null);\n    }\n  }\n\n  componentWillMount() {\n    if (this.props.match.params.slug) {\n      return this.props.onLoad(agent.Articles.get(this.props.match.params.slug));\n    }\n    this.props.onLoad(null);\n  }\n\n  componentWillUnmount() {\n    this.props.onUnload();\n  }\n\n  render() {\n    return (\n      <div className=\"editor-page\">\n        <div className=\"container page\">\n          <div className=\"row\">\n            <div className=\"col-md-10 offset-md-1 col-xs-12\">\n\n              <ListErrors errors={this.props.errors}></ListErrors>\n\n              <form>\n                <fieldset>\n\n                  <fieldset className=\"form-group\">\n                    <input\n                      className=\"form-control form-control-lg\"\n                      type=\"text\"\n                      placeholder=\"Article Title\"\n                      value={this.props.title}\n                      onChange={this.changeTitle} />\n                  </fieldset>\n\n                  <fieldset className=\"form-group\">\n                    <input\n                      className=\"form-control\"\n                      type=\"text\"\n                      placeholder=\"What's this article about?\"\n                      value={this.props.description}\n                      onChange={this.changeDescription} />\n                  </fieldset>\n\n                  <fieldset className=\"form-group\">\n                    <textarea\n                      className=\"form-control\"\n                      rows=\"8\"\n                      placeholder=\"Write your article (in markdown)\"\n                      value={this.props.body}\n                      onChange={this.changeBody}>\n                    </textarea>\n                  </fieldset>\n\n                  <fieldset className=\"form-group\">\n                    <input\n                      className=\"form-control\"\n                      type=\"text\"\n                      placeholder=\"Enter tags\"\n                      value={this.props.tagInput}\n                      onChange={this.changeTagInput}\n                      onKeyUp={this.watchForEnter} />\n\n                    <div className=\"tag-list\">\n                      {\n                        (this.props.tagList || []).map(tag => {\n                          return (\n                            <span className=\"tag-default tag-pill\" key={tag}>\n                              <i  className=\"ion-close-round\"\n                                  onClick={this.removeTagHandler(tag)}>\n                              </i>\n                              {tag}\n                            </span>\n                          );\n                        })\n                      }\n                    </div>\n                  </fieldset>\n\n                  <button\n                    className=\"btn btn-lg pull-xs-right btn-primary\"\n                    type=\"button\"\n                    disabled={this.props.inProgress}\n                    onClick={this.submitForm}>\n                    Publish Article\n                  </button>\n\n                </fieldset>\n              </form>\n\n            </div>\n          </div>\n        </div>\n      </div>\n    );\n  }\n}\n\nexport default connect(mapStateToProps, mapDispatchToProps)(Editor);\n"
  },
  {
    "path": "src/components/Header.js",
    "content": "import React from 'react';\nimport { Link } from 'react-router-dom';\n\nconst LoggedOutView = props => {\n  if (!props.currentUser) {\n    return (\n      <ul className=\"nav navbar-nav pull-xs-right\">\n\n        <li className=\"nav-item\">\n          <Link to=\"/\" className=\"nav-link\">\n            Home\n          </Link>\n        </li>\n\n        <li className=\"nav-item\">\n          <Link to=\"/login\" className=\"nav-link\">\n            Sign in\n          </Link>\n        </li>\n\n        <li className=\"nav-item\">\n          <Link to=\"/register\" className=\"nav-link\">\n            Sign up\n          </Link>\n        </li>\n\n      </ul>\n    );\n  }\n  return null;\n};\n\nconst LoggedInView = props => {\n  if (props.currentUser) {\n    return (\n      <ul className=\"nav navbar-nav pull-xs-right\">\n\n        <li className=\"nav-item\">\n          <Link to=\"/\" className=\"nav-link\">\n            Home\n          </Link>\n        </li>\n\n        <li className=\"nav-item\">\n          <Link to=\"/editor\" className=\"nav-link\">\n            <i className=\"ion-compose\"></i>&nbsp;New Post\n          </Link>\n        </li>\n\n        <li className=\"nav-item\">\n          <Link to=\"/settings\" className=\"nav-link\">\n            <i className=\"ion-gear-a\"></i>&nbsp;Settings\n          </Link>\n        </li>\n\n        <li className=\"nav-item\">\n          <Link\n            to={`/@${props.currentUser.username}`}\n            className=\"nav-link\">\n            <img src={props.currentUser.image} className=\"user-pic\" alt={props.currentUser.username} />\n            {props.currentUser.username}\n          </Link>\n        </li>\n\n      </ul>\n    );\n  }\n\n  return null;\n};\n\nclass Header extends React.Component {\n  render() {\n    return (\n      <nav className=\"navbar navbar-light\">\n        <div className=\"container\">\n\n          <Link to=\"/\" className=\"navbar-brand\">\n            {this.props.appName.toLowerCase()}\n          </Link>\n\n          <LoggedOutView currentUser={this.props.currentUser} />\n\n          <LoggedInView currentUser={this.props.currentUser} />\n        </div>\n      </nav>\n    );\n  }\n}\n\nexport default Header;\n"
  },
  {
    "path": "src/components/Home/Banner.js",
    "content": "import React from 'react';\n\nconst Banner = ({ appName, token }) => {\n  if (token) {\n    return null;\n  }\n  return (\n    <div className=\"banner\">\n      <div className=\"container\">\n        <h1 className=\"logo-font\">\n          {appName.toLowerCase()}\n        </h1>\n        <p>A place to share your knowledge.</p>\n      </div>\n    </div>\n  );\n};\n\nexport default Banner;\n"
  },
  {
    "path": "src/components/Home/MainView.js",
    "content": "import ArticleList from '../ArticleList';\nimport React from 'react';\nimport agent from '../../agent';\nimport { connect } from 'react-redux';\nimport { CHANGE_TAB } from '../../constants/actionTypes';\n\nconst YourFeedTab = props => {\n  if (props.token) {\n    const clickHandler = ev => {\n      ev.preventDefault();\n      props.onTabClick('feed', agent.Articles.feed, agent.Articles.feed());\n    }\n\n    return (\n      <li className=\"nav-item\">\n        <a  href=\"\"\n            className={ props.tab === 'feed' ? 'nav-link active' : 'nav-link' }\n            onClick={clickHandler}>\n          Your Feed\n        </a>\n      </li>\n    );\n  }\n  return null;\n};\n\nconst GlobalFeedTab = props => {\n  const clickHandler = ev => {\n    ev.preventDefault();\n    props.onTabClick('all', agent.Articles.all, agent.Articles.all());\n  };\n  return (\n    <li className=\"nav-item\">\n      <a\n        href=\"\"\n        className={ props.tab === 'all' ? 'nav-link active' : 'nav-link' }\n        onClick={clickHandler}>\n        Global Feed\n      </a>\n    </li>\n  );\n};\n\nconst TagFilterTab = props => {\n  if (!props.tag) {\n    return null;\n  }\n\n  return (\n    <li className=\"nav-item\">\n      <a href=\"\" className=\"nav-link active\">\n        <i className=\"ion-pound\"></i> {props.tag}\n      </a>\n    </li>\n  );\n};\n\nconst mapStateToProps = state => ({\n  ...state.articleList,\n  tags: state.home.tags,\n  token: state.common.token\n});\n\nconst mapDispatchToProps = dispatch => ({\n  onTabClick: (tab, pager, payload) => dispatch({ type: CHANGE_TAB, tab, pager, payload })\n});\n\nconst MainView = props => {\n  return (\n    <div className=\"col-md-9\">\n      <div className=\"feed-toggle\">\n        <ul className=\"nav nav-pills outline-active\">\n\n          <YourFeedTab\n            token={props.token}\n            tab={props.tab}\n            onTabClick={props.onTabClick} />\n\n          <GlobalFeedTab tab={props.tab} onTabClick={props.onTabClick} />\n\n          <TagFilterTab tag={props.tag} />\n\n        </ul>\n      </div>\n\n      <ArticleList\n        pager={props.pager}\n        articles={props.articles}\n        loading={props.loading}\n        articlesCount={props.articlesCount}\n        currentPage={props.currentPage} />\n    </div>\n  );\n};\n\nexport default connect(mapStateToProps, mapDispatchToProps)(MainView);\n"
  },
  {
    "path": "src/components/Home/Tags.js",
    "content": "import React from 'react';\nimport agent from '../../agent';\n\nconst Tags = props => {\n  const tags = props.tags;\n  if (tags) {\n    return (\n      <div className=\"tag-list\">\n        {\n          tags.map(tag => {\n            const handleClick = ev => {\n              ev.preventDefault();\n              props.onClickTag(tag, page => agent.Articles.byTag(tag, page), agent.Articles.byTag(tag));\n            };\n\n            return (\n              <a\n                href=\"\"\n                className=\"tag-default tag-pill\"\n                key={tag}\n                onClick={handleClick}>\n                {tag}\n              </a>\n            );\n          })\n        }\n      </div>\n    );\n  } else {\n    return (\n      <div>Loading Tags...</div>\n    );\n  }\n};\n\nexport default Tags;\n"
  },
  {
    "path": "src/components/Home/index.js",
    "content": "import Banner from './Banner';\nimport MainView from './MainView';\nimport React from 'react';\nimport Tags from './Tags';\nimport agent from '../../agent';\nimport { connect } from 'react-redux';\nimport {\n  HOME_PAGE_LOADED,\n  HOME_PAGE_UNLOADED,\n  APPLY_TAG_FILTER\n} from '../../constants/actionTypes';\n\nconst Promise = global.Promise;\n\nconst mapStateToProps = state => ({\n  ...state.home,\n  appName: state.common.appName,\n  token: state.common.token\n});\n\nconst mapDispatchToProps = dispatch => ({\n  onClickTag: (tag, pager, payload) =>\n    dispatch({ type: APPLY_TAG_FILTER, tag, pager, payload }),\n  onLoad: (tab, pager, payload) =>\n    dispatch({ type: HOME_PAGE_LOADED, tab, pager, payload }),\n  onUnload: () =>\n    dispatch({  type: HOME_PAGE_UNLOADED })\n});\n\nclass Home extends React.Component {\n  componentWillMount() {\n    const tab = this.props.token ? 'feed' : 'all';\n    const articlesPromise = this.props.token ?\n      agent.Articles.feed :\n      agent.Articles.all;\n\n    this.props.onLoad(tab, articlesPromise, Promise.all([agent.Tags.getAll(), articlesPromise()]));\n  }\n\n  componentWillUnmount() {\n    this.props.onUnload();\n  }\n\n  render() {\n    return (\n      <div className=\"home-page\">\n\n        <Banner token={this.props.token} appName={this.props.appName} />\n\n        <div className=\"container page\">\n          <div className=\"row\">\n            <MainView />\n\n            <div className=\"col-md-3\">\n              <div className=\"sidebar\">\n\n                <p>Popular Tags</p>\n\n                <Tags\n                  tags={this.props.tags}\n                  onClickTag={this.props.onClickTag} />\n\n              </div>\n            </div>\n          </div>\n        </div>\n\n      </div>\n    );\n  }\n}\n\nexport default connect(mapStateToProps, mapDispatchToProps)(Home);\n"
  },
  {
    "path": "src/components/ListErrors.js",
    "content": "import React from 'react';\n\nclass ListErrors extends React.Component {\n  render() {\n    const errors = this.props.errors;\n    if (errors) {\n      return (\n        <ul className=\"error-messages\">\n          {\n            Object.keys(errors).map(key => {\n              return (\n                <li key={key}>\n                  {key} {errors[key]}\n                </li>\n              );\n            })\n          }\n        </ul>\n      );\n    } else {\n      return null;\n    }\n  }\n}\n\nexport default ListErrors;\n"
  },
  {
    "path": "src/components/ListPagination.js",
    "content": "import React from 'react';\nimport agent from '../agent';\nimport { connect } from 'react-redux';\nimport { SET_PAGE } from '../constants/actionTypes';\n\nconst mapDispatchToProps = dispatch => ({\n  onSetPage: (page, payload) =>\n    dispatch({ type: SET_PAGE, page, payload })\n});\n\nconst ListPagination = props => {\n  if (props.articlesCount <= 10) {\n    return null;\n  }\n\n  const range = [];\n  for (let i = 0; i < Math.ceil(props.articlesCount / 10); ++i) {\n    range.push(i);\n  }\n\n  const setPage = page => {\n    if(props.pager) {\n      props.onSetPage(page, props.pager(page));\n    }else {\n      props.onSetPage(page, agent.Articles.all(page))\n    }\n  };\n\n  return (\n    <nav>\n      <ul className=\"pagination\">\n\n        {\n          range.map(v => {\n            const isCurrent = v === props.currentPage;\n            const onClick = ev => {\n              ev.preventDefault();\n              setPage(v);\n            };\n            return (\n              <li\n                className={ isCurrent ? 'page-item active' : 'page-item' }\n                onClick={onClick}\n                key={v.toString()}>\n\n                <a className=\"page-link\" href=\"\">{v + 1}</a>\n\n              </li>\n            );\n          })\n        }\n\n      </ul>\n    </nav>\n  );\n};\n\nexport default connect(() => ({}), mapDispatchToProps)(ListPagination);\n"
  },
  {
    "path": "src/components/Login.js",
    "content": "import { Link } from 'react-router-dom';\nimport ListErrors from './ListErrors';\nimport React from 'react';\nimport agent from '../agent';\nimport { connect } from 'react-redux';\nimport {\n  UPDATE_FIELD_AUTH,\n  LOGIN,\n  LOGIN_PAGE_UNLOADED\n} from '../constants/actionTypes';\n\nconst mapStateToProps = state => ({ ...state.auth });\n\nconst mapDispatchToProps = dispatch => ({\n  onChangeEmail: value =>\n    dispatch({ type: UPDATE_FIELD_AUTH, key: 'email', value }),\n  onChangePassword: value =>\n    dispatch({ type: UPDATE_FIELD_AUTH, key: 'password', value }),\n  onSubmit: (email, password) =>\n    dispatch({ type: LOGIN, payload: agent.Auth.login(email, password) }),\n  onUnload: () =>\n    dispatch({ type: LOGIN_PAGE_UNLOADED })\n});\n\nclass Login extends React.Component {\n  constructor() {\n    super();\n    this.changeEmail = ev => this.props.onChangeEmail(ev.target.value);\n    this.changePassword = ev => this.props.onChangePassword(ev.target.value);\n    this.submitForm = (email, password) => ev => {\n      ev.preventDefault();\n      this.props.onSubmit(email, password);\n    };\n  }\n\n  componentWillUnmount() {\n    this.props.onUnload();\n  }\n\n  render() {\n    const email = this.props.email;\n    const password = this.props.password;\n    return (\n      <div className=\"auth-page\">\n        <div className=\"container page\">\n          <div className=\"row\">\n\n            <div className=\"col-md-6 offset-md-3 col-xs-12\">\n              <h1 className=\"text-xs-center\">Sign In</h1>\n              <p className=\"text-xs-center\">\n                <Link to=\"/register\">\n                  Need an account?\n                </Link>\n              </p>\n\n              <ListErrors errors={this.props.errors} />\n\n              <form onSubmit={this.submitForm(email, password)}>\n                <fieldset>\n\n                  <fieldset className=\"form-group\">\n                    <input\n                      className=\"form-control form-control-lg\"\n                      type=\"email\"\n                      placeholder=\"Email\"\n                      value={email}\n                      onChange={this.changeEmail} />\n                  </fieldset>\n\n                  <fieldset className=\"form-group\">\n                    <input\n                      className=\"form-control form-control-lg\"\n                      type=\"password\"\n                      placeholder=\"Password\"\n                      value={password}\n                      onChange={this.changePassword} />\n                  </fieldset>\n\n                  <button\n                    className=\"btn btn-lg btn-primary pull-xs-right\"\n                    type=\"submit\"\n                    disabled={this.props.inProgress}>\n                    Sign in\n                  </button>\n\n                </fieldset>\n              </form>\n            </div>\n\n          </div>\n        </div>\n      </div>\n    );\n  }\n}\n\nexport default connect(mapStateToProps, mapDispatchToProps)(Login);\n"
  },
  {
    "path": "src/components/Profile.js",
    "content": "import ArticleList from './ArticleList';\nimport React from 'react';\nimport { Link } from 'react-router-dom';\nimport agent from '../agent';\nimport { connect } from 'react-redux';\nimport {\n  FOLLOW_USER,\n  UNFOLLOW_USER,\n  PROFILE_PAGE_LOADED,\n  PROFILE_PAGE_UNLOADED\n} from '../constants/actionTypes';\n\nconst EditProfileSettings = props => {\n  if (props.isUser) {\n    return (\n      <Link\n        to=\"/settings\"\n        className=\"btn btn-sm btn-outline-secondary action-btn\">\n        <i className=\"ion-gear-a\"></i> Edit Profile Settings\n      </Link>\n    );\n  }\n  return null;\n};\n\nconst FollowUserButton = props => {\n  if (props.isUser) {\n    return null;\n  }\n\n  let classes = 'btn btn-sm action-btn';\n  if (props.user.following) {\n    classes += ' btn-secondary';\n  } else {\n    classes += ' btn-outline-secondary';\n  }\n\n  const handleClick = ev => {\n    ev.preventDefault();\n    if (props.user.following) {\n      props.unfollow(props.user.username)\n    } else {\n      props.follow(props.user.username)\n    }\n  };\n\n  return (\n    <button\n      className={classes}\n      onClick={handleClick}>\n      <i className=\"ion-plus-round\"></i>\n      &nbsp;\n      {props.user.following ? 'Unfollow' : 'Follow'} {props.user.username}\n    </button>\n  );\n};\n\nconst mapStateToProps = state => ({\n  ...state.articleList,\n  currentUser: state.common.currentUser,\n  profile: state.profile\n});\n\nconst mapDispatchToProps = dispatch => ({\n  onFollow: username => dispatch({\n    type: FOLLOW_USER,\n    payload: agent.Profile.follow(username)\n  }),\n  onLoad: payload => dispatch({ type: PROFILE_PAGE_LOADED, payload }),\n  onUnfollow: username => dispatch({\n    type: UNFOLLOW_USER,\n    payload: agent.Profile.unfollow(username)\n  }),\n  onUnload: () => dispatch({ type: PROFILE_PAGE_UNLOADED })\n});\n\nclass Profile extends React.Component {\n  componentWillMount() {\n    this.props.onLoad(Promise.all([\n      agent.Profile.get(this.props.match.params.username),\n      agent.Articles.byAuthor(this.props.match.params.username)\n    ]));\n  }\n\n  componentWillUnmount() {\n    this.props.onUnload();\n  }\n\n  renderTabs() {\n    return (\n      <ul className=\"nav nav-pills outline-active\">\n        <li className=\"nav-item\">\n          <Link\n            className=\"nav-link active\"\n            to={`/@${this.props.profile.username}`}>\n            My Articles\n          </Link>\n        </li>\n\n        <li className=\"nav-item\">\n          <Link\n            className=\"nav-link\"\n            to={`/@${this.props.profile.username}/favorites`}>\n            Favorited Articles\n          </Link>\n        </li>\n      </ul>\n    );\n  }\n\n  render() {\n    const profile = this.props.profile;\n    if (!profile) {\n      return null;\n    }\n\n    const isUser = this.props.currentUser &&\n      this.props.profile.username === this.props.currentUser.username;\n\n    return (\n      <div className=\"profile-page\">\n\n        <div className=\"user-info\">\n          <div className=\"container\">\n            <div className=\"row\">\n              <div className=\"col-xs-12 col-md-10 offset-md-1\">\n\n                <img src={profile.image} className=\"user-img\" alt={profile.username} />\n                <h4>{profile.username}</h4>\n                <p>{profile.bio}</p>\n\n                <EditProfileSettings isUser={isUser} />\n                <FollowUserButton\n                  isUser={isUser}\n                  user={profile}\n                  follow={this.props.onFollow}\n                  unfollow={this.props.onUnfollow}\n                  />\n\n              </div>\n            </div>\n          </div>\n        </div>\n\n        <div className=\"container\">\n          <div className=\"row\">\n\n            <div className=\"col-xs-12 col-md-10 offset-md-1\">\n\n              <div className=\"articles-toggle\">\n                {this.renderTabs()}\n              </div>\n\n              <ArticleList\n                pager={this.props.pager}\n                articles={this.props.articles}\n                articlesCount={this.props.articlesCount}\n                state={this.props.currentPage} />\n            </div>\n\n          </div>\n        </div>\n\n      </div>\n    );\n  }\n}\n\nexport default connect(mapStateToProps, mapDispatchToProps)(Profile);\nexport { Profile, mapStateToProps };\n"
  },
  {
    "path": "src/components/ProfileFavorites.js",
    "content": "import { Profile, mapStateToProps } from './Profile';\nimport React from 'react';\nimport { Link } from 'react-router-dom';\nimport agent from '../agent';\nimport { connect } from 'react-redux';\nimport {\n  PROFILE_PAGE_LOADED,\n  PROFILE_PAGE_UNLOADED\n} from '../constants/actionTypes';\n\nconst mapDispatchToProps = dispatch => ({\n  onLoad: (pager, payload) =>\n    dispatch({ type: PROFILE_PAGE_LOADED, pager, payload }),\n  onUnload: () =>\n    dispatch({ type: PROFILE_PAGE_UNLOADED })\n});\n\nclass ProfileFavorites extends Profile {\n  componentWillMount() {\n    this.props.onLoad(page => agent.Articles.favoritedBy(this.props.match.params.username, page), Promise.all([\n      agent.Profile.get(this.props.match.params.username),\n      agent.Articles.favoritedBy(this.props.match.params.username)\n    ]));\n  }\n\n  componentWillUnmount() {\n    this.props.onUnload();\n  }\n\n  renderTabs() {\n    return (\n      <ul className=\"nav nav-pills outline-active\">\n        <li className=\"nav-item\">\n          <Link\n            className=\"nav-link\"\n            to={`/@${this.props.profile.username}`}>\n            My Articles\n          </Link>\n        </li>\n\n        <li className=\"nav-item\">\n          <Link\n            className=\"nav-link active\"\n            to={`/@${this.props.profile.username}/favorites`}>\n            Favorited Articles\n          </Link>\n        </li>\n      </ul>\n    );\n  }\n}\n\nexport default connect(mapStateToProps, mapDispatchToProps)(ProfileFavorites);\n"
  },
  {
    "path": "src/components/Register.js",
    "content": "import { Link } from 'react-router-dom';\nimport ListErrors from './ListErrors';\nimport React from 'react';\nimport agent from '../agent';\nimport { connect } from 'react-redux';\nimport {\n  UPDATE_FIELD_AUTH,\n  REGISTER,\n  REGISTER_PAGE_UNLOADED\n} from '../constants/actionTypes';\n\nconst mapStateToProps = state => ({ ...state.auth });\n\nconst mapDispatchToProps = dispatch => ({\n  onChangeEmail: value =>\n    dispatch({ type: UPDATE_FIELD_AUTH, key: 'email', value }),\n  onChangePassword: value =>\n    dispatch({ type: UPDATE_FIELD_AUTH, key: 'password', value }),\n  onChangeUsername: value =>\n    dispatch({ type: UPDATE_FIELD_AUTH, key: 'username', value }),\n  onSubmit: (username, email, password) => {\n    const payload = agent.Auth.register(username, email, password);\n    dispatch({ type: REGISTER, payload })\n  },\n  onUnload: () =>\n    dispatch({ type: REGISTER_PAGE_UNLOADED })\n});\n\nclass Register extends React.Component {\n  constructor() {\n    super();\n    this.changeEmail = ev => this.props.onChangeEmail(ev.target.value);\n    this.changePassword = ev => this.props.onChangePassword(ev.target.value);\n    this.changeUsername = ev => this.props.onChangeUsername(ev.target.value);\n    this.submitForm = (username, email, password) => ev => {\n      ev.preventDefault();\n      this.props.onSubmit(username, email, password);\n    }\n  }\n\n  componentWillUnmount() {\n    this.props.onUnload();\n  }\n\n  render() {\n    const email = this.props.email;\n    const password = this.props.password;\n    const username = this.props.username;\n\n    return (\n      <div className=\"auth-page\">\n        <div className=\"container page\">\n          <div className=\"row\">\n\n            <div className=\"col-md-6 offset-md-3 col-xs-12\">\n              <h1 className=\"text-xs-center\">Sign Up</h1>\n              <p className=\"text-xs-center\">\n                <Link to=\"/login\">\n                  Have an account?\n                </Link>\n              </p>\n\n              <ListErrors errors={this.props.errors} />\n\n              <form onSubmit={this.submitForm(username, email, password)}>\n                <fieldset>\n\n                  <fieldset className=\"form-group\">\n                    <input\n                      className=\"form-control form-control-lg\"\n                      type=\"text\"\n                      placeholder=\"Username\"\n                      value={this.props.username}\n                      onChange={this.changeUsername} />\n                  </fieldset>\n\n                  <fieldset className=\"form-group\">\n                    <input\n                      className=\"form-control form-control-lg\"\n                      type=\"email\"\n                      placeholder=\"Email\"\n                      value={this.props.email}\n                      onChange={this.changeEmail} />\n                  </fieldset>\n\n                  <fieldset className=\"form-group\">\n                    <input\n                      className=\"form-control form-control-lg\"\n                      type=\"password\"\n                      placeholder=\"Password\"\n                      value={this.props.password}\n                      onChange={this.changePassword} />\n                  </fieldset>\n\n                  <button\n                    className=\"btn btn-lg btn-primary pull-xs-right\"\n                    type=\"submit\"\n                    disabled={this.props.inProgress}>\n                    Sign up\n                  </button>\n\n                </fieldset>\n              </form>\n            </div>\n\n          </div>\n        </div>\n      </div>\n    );\n  }\n}\n\nexport default connect(mapStateToProps, mapDispatchToProps)(Register);\n"
  },
  {
    "path": "src/components/Settings.js",
    "content": "import ListErrors from './ListErrors';\nimport React from 'react';\nimport agent from '../agent';\nimport { connect } from 'react-redux';\nimport {\n  SETTINGS_SAVED,\n  SETTINGS_PAGE_UNLOADED,\n  LOGOUT\n} from '../constants/actionTypes';\n\nclass SettingsForm extends React.Component {\n  constructor() {\n    super();\n\n    this.state = {\n      image: '',\n      username: '',\n      bio: '',\n      email: '',\n      password: ''\n    };\n\n    this.updateState = field => ev => {\n      const state = this.state;\n      const newState = Object.assign({}, state, { [field]: ev.target.value });\n      this.setState(newState);\n    };\n\n    this.submitForm = ev => {\n      ev.preventDefault();\n\n      const user = Object.assign({}, this.state);\n      if (!user.password) {\n        delete user.password;\n      }\n\n      this.props.onSubmitForm(user);\n    };\n  }\n\n  componentWillMount() {\n    if (this.props.currentUser) {\n      Object.assign(this.state, {\n        image: this.props.currentUser.image || '',\n        username: this.props.currentUser.username,\n        bio: this.props.currentUser.bio,\n        email: this.props.currentUser.email\n      });\n    }\n  }\n\n  componentWillReceiveProps(nextProps) {\n    if (nextProps.currentUser) {\n      this.setState(Object.assign({}, this.state, {\n        image: nextProps.currentUser.image || '',\n        username: nextProps.currentUser.username,\n        bio: nextProps.currentUser.bio,\n        email: nextProps.currentUser.email\n      }));\n    }\n  }\n\n  render() {\n    return (\n      <form onSubmit={this.submitForm}>\n        <fieldset>\n\n          <fieldset className=\"form-group\">\n            <input\n              className=\"form-control\"\n              type=\"text\"\n              placeholder=\"URL of profile picture\"\n              value={this.state.image}\n              onChange={this.updateState('image')} />\n          </fieldset>\n\n          <fieldset className=\"form-group\">\n            <input\n              className=\"form-control form-control-lg\"\n              type=\"text\"\n              placeholder=\"Username\"\n              value={this.state.username}\n              onChange={this.updateState('username')} />\n          </fieldset>\n\n          <fieldset className=\"form-group\">\n            <textarea\n              className=\"form-control form-control-lg\"\n              rows=\"8\"\n              placeholder=\"Short bio about you\"\n              value={this.state.bio}\n              onChange={this.updateState('bio')}>\n            </textarea>\n          </fieldset>\n\n          <fieldset className=\"form-group\">\n            <input\n              className=\"form-control form-control-lg\"\n              type=\"email\"\n              placeholder=\"Email\"\n              value={this.state.email}\n              onChange={this.updateState('email')} />\n          </fieldset>\n\n          <fieldset className=\"form-group\">\n            <input\n              className=\"form-control form-control-lg\"\n              type=\"password\"\n              placeholder=\"New Password\"\n              value={this.state.password}\n              onChange={this.updateState('password')} />\n          </fieldset>\n\n          <button\n            className=\"btn btn-lg btn-primary pull-xs-right\"\n            type=\"submit\"\n            disabled={this.state.inProgress}>\n            Update Settings\n          </button>\n\n        </fieldset>\n      </form>\n    );\n  }\n}\n\nconst mapStateToProps = state => ({\n  ...state.settings,\n  currentUser: state.common.currentUser\n});\n\nconst mapDispatchToProps = dispatch => ({\n  onClickLogout: () => dispatch({ type: LOGOUT }),\n  onSubmitForm: user =>\n    dispatch({ type: SETTINGS_SAVED, payload: agent.Auth.save(user) }),\n  onUnload: () => dispatch({ type: SETTINGS_PAGE_UNLOADED })\n});\n\nclass Settings extends React.Component {\n  render() {\n    return (\n      <div className=\"settings-page\">\n        <div className=\"container page\">\n          <div className=\"row\">\n            <div className=\"col-md-6 offset-md-3 col-xs-12\">\n\n              <h1 className=\"text-xs-center\">Your Settings</h1>\n\n              <ListErrors errors={this.props.errors}></ListErrors>\n\n              <SettingsForm\n                currentUser={this.props.currentUser}\n                onSubmitForm={this.props.onSubmitForm} />\n\n              <hr />\n\n              <button\n                className=\"btn btn-outline-danger\"\n                onClick={this.props.onClickLogout}>\n                Or click here to logout.\n              </button>\n\n            </div>\n          </div>\n        </div>\n      </div>\n    );\n  }\n}\n\nexport default connect(mapStateToProps, mapDispatchToProps)(Settings);\n"
  },
  {
    "path": "src/constants/actionTypes.js",
    "content": "export const APP_LOAD = 'APP_LOAD';\nexport const REDIRECT = 'REDIRECT';\nexport const ARTICLE_SUBMITTED = 'ARTICLE_SUBMITTED';\nexport const SETTINGS_SAVED = 'SETTINGS_SAVED';\nexport const DELETE_ARTICLE = 'DELETE_ARTICLE';\nexport const SETTINGS_PAGE_UNLOADED = 'SETTINGS_PAGE_UNLOADED';\nexport const HOME_PAGE_LOADED = 'HOME_PAGE_LOADED';\nexport const HOME_PAGE_UNLOADED = 'HOME_PAGE_UNLOADED';\nexport const ARTICLE_PAGE_LOADED = 'ARTICLE_PAGE_LOADED';\nexport const ARTICLE_PAGE_UNLOADED = 'ARTICLE_PAGE_UNLOADED';\nexport const ADD_COMMENT = 'ADD_COMMENT';\nexport const DELETE_COMMENT = 'DELETE_COMMENT';\nexport const ARTICLE_FAVORITED = 'ARTICLE_FAVORITED';\nexport const ARTICLE_UNFAVORITED = 'ARTICLE_UNFAVORITED';\nexport const SET_PAGE = 'SET_PAGE';\nexport const APPLY_TAG_FILTER = 'APPLY_TAG_FILTER';\nexport const CHANGE_TAB = 'CHANGE_TAB';\nexport const PROFILE_PAGE_LOADED = 'PROFILE_PAGE_LOADED';\nexport const PROFILE_PAGE_UNLOADED = 'PROFILE_PAGE_UNLOADED';\nexport const LOGIN = 'LOGIN';\nexport const LOGOUT = 'LOGOUT';\nexport const REGISTER = 'REGISTER';\nexport const LOGIN_PAGE_UNLOADED = 'LOGIN_PAGE_UNLOADED';\nexport const REGISTER_PAGE_UNLOADED = 'REGISTER_PAGE_UNLOADED';\nexport const ASYNC_START = 'ASYNC_START';\nexport const ASYNC_END = 'ASYNC_END';\nexport const EDITOR_PAGE_LOADED = 'EDITOR_PAGE_LOADED';\nexport const EDITOR_PAGE_UNLOADED = 'EDITOR_PAGE_UNLOADED';\nexport const ADD_TAG = 'ADD_TAG';\nexport const REMOVE_TAG = 'REMOVE_TAG';\nexport const UPDATE_FIELD_AUTH = 'UPDATE_FIELD_AUTH';\nexport const UPDATE_FIELD_EDITOR = 'UPDATE_FIELD_EDITOR';\nexport const FOLLOW_USER = 'FOLLOW_USER';\nexport const UNFOLLOW_USER = 'UNFOLLOW_USER';\nexport const PROFILE_FAVORITES_PAGE_UNLOADED = 'PROFILE_FAVORITES_PAGE_UNLOADED';\nexport const PROFILE_FAVORITES_PAGE_LOADED = 'PROFILE_FAVORITES_PAGE_LOADED';"
  },
  {
    "path": "src/index.js",
    "content": "import ReactDOM from 'react-dom';\nimport { Provider } from 'react-redux';\nimport React from 'react';\nimport { store, history} from './store';\n\nimport { Route, Switch } from 'react-router-dom';\nimport { ConnectedRouter } from 'react-router-redux';\n\nimport App from './components/App';\n\nReactDOM.render((\n  <Provider store={store}>\n    <ConnectedRouter history={history}>\n      <Switch>\n        <Route path=\"/\" component={App} />\n      </Switch>\n    </ConnectedRouter>\n  </Provider>\n\n), document.getElementById('root'));\n"
  },
  {
    "path": "src/middleware.js",
    "content": "import agent from './agent';\nimport {\n  ASYNC_START,\n  ASYNC_END,\n  LOGIN,\n  LOGOUT,\n  REGISTER\n} from './constants/actionTypes';\n\nconst promiseMiddleware = store => next => action => {\n  if (isPromise(action.payload)) {\n    store.dispatch({ type: ASYNC_START, subtype: action.type });\n\n    const currentView = store.getState().viewChangeCounter;\n    const skipTracking = action.skipTracking;\n\n    action.payload.then(\n      res => {\n        const currentState = store.getState()\n        if (!skipTracking && currentState.viewChangeCounter !== currentView) {\n          return\n        }\n        console.log('RESULT', res);\n        action.payload = res;\n        store.dispatch({ type: ASYNC_END, promise: action.payload });\n        store.dispatch(action);\n      },\n      error => {\n        const currentState = store.getState()\n        if (!skipTracking && currentState.viewChangeCounter !== currentView) {\n          return\n        }\n        console.log('ERROR', error);\n        action.error = true;\n        action.payload = error.response.body;\n        if (!action.skipTracking) {\n          store.dispatch({ type: ASYNC_END, promise: action.payload });\n        }\n        store.dispatch(action);\n      }\n    );\n\n    return;\n  }\n\n  next(action);\n};\n\nconst localStorageMiddleware = store => next => action => {\n  if (action.type === REGISTER || action.type === LOGIN) {\n    if (!action.error) {\n      window.localStorage.setItem('jwt', action.payload.user.token);\n      agent.setToken(action.payload.user.token);\n    }\n  } else if (action.type === LOGOUT) {\n    window.localStorage.setItem('jwt', '');\n    agent.setToken(null);\n  }\n\n  next(action);\n};\n\nfunction isPromise(v) {\n  return v && typeof v.then === 'function';\n}\n\n\nexport { promiseMiddleware, localStorageMiddleware }\n"
  },
  {
    "path": "src/reducer.js",
    "content": "import article from './reducers/article';\nimport articleList from './reducers/articleList';\nimport auth from './reducers/auth';\nimport { combineReducers } from 'redux';\nimport common from './reducers/common';\nimport editor from './reducers/editor';\nimport home from './reducers/home';\nimport profile from './reducers/profile';\nimport settings from './reducers/settings';\nimport { routerReducer } from 'react-router-redux';\n\nexport default combineReducers({\n  article,\n  articleList,\n  auth,\n  common,\n  editor,\n  home,\n  profile,\n  settings,\n  router: routerReducer\n});\n"
  },
  {
    "path": "src/reducers/article.js",
    "content": "import {\n  ARTICLE_PAGE_LOADED,\n  ARTICLE_PAGE_UNLOADED,\n  ADD_COMMENT,\n  DELETE_COMMENT\n} from '../constants/actionTypes';\n\nexport default (state = {}, action) => {\n  switch (action.type) {\n    case ARTICLE_PAGE_LOADED:\n      return {\n        ...state,\n        article: action.payload[0].article,\n        comments: action.payload[1].comments\n      };\n    case ARTICLE_PAGE_UNLOADED:\n      return {};\n    case ADD_COMMENT:\n      return {\n        ...state,\n        commentErrors: action.error ? action.payload.errors : null,\n        comments: action.error ?\n          null :\n          (state.comments || []).concat([action.payload.comment])\n      };\n    case DELETE_COMMENT:\n      const commentId = action.commentId\n      return {\n        ...state,\n        comments: state.comments.filter(comment => comment.id !== commentId)\n      };\n    default:\n      return state;\n  }\n};\n"
  },
  {
    "path": "src/reducers/articleList.js",
    "content": "import {\n  ARTICLE_FAVORITED,\n  ARTICLE_UNFAVORITED,\n  SET_PAGE,\n  APPLY_TAG_FILTER,\n  HOME_PAGE_LOADED,\n  HOME_PAGE_UNLOADED,\n  CHANGE_TAB,\n  PROFILE_PAGE_LOADED,\n  PROFILE_PAGE_UNLOADED,\n  PROFILE_FAVORITES_PAGE_LOADED,\n  PROFILE_FAVORITES_PAGE_UNLOADED\n} from '../constants/actionTypes';\n\nexport default (state = {}, action) => {\n  switch (action.type) {\n    case ARTICLE_FAVORITED:\n    case ARTICLE_UNFAVORITED:\n      return {\n        ...state,\n        articles: state.articles.map(article => {\n          if (article.slug === action.payload.article.slug) {\n            return {\n              ...article,\n              favorited: action.payload.article.favorited,\n              favoritesCount: action.payload.article.favoritesCount\n            };\n          }\n          return article;\n        })\n      };\n    case SET_PAGE:\n      return {\n        ...state,\n        articles: action.payload.articles,\n        articlesCount: action.payload.articlesCount,\n        currentPage: action.page\n      };\n    case APPLY_TAG_FILTER:\n      return {\n        ...state,\n        pager: action.pager,\n        articles: action.payload.articles,\n        articlesCount: action.payload.articlesCount,\n        tab: null,\n        tag: action.tag,\n        currentPage: 0\n      };\n    case HOME_PAGE_LOADED:\n      return {\n        ...state,\n        pager: action.pager,\n        tags: action.payload[0].tags,\n        articles: action.payload[1].articles,\n        articlesCount: action.payload[1].articlesCount,\n        currentPage: 0,\n        tab: action.tab\n      };\n    case HOME_PAGE_UNLOADED:\n      return {};\n    case CHANGE_TAB:\n      return {\n        ...state,\n        pager: action.pager,\n        articles: action.payload.articles,\n        articlesCount: action.payload.articlesCount,\n        tab: action.tab,\n        currentPage: 0,\n        tag: null\n      };\n    case PROFILE_PAGE_LOADED:\n    case PROFILE_FAVORITES_PAGE_LOADED:\n      return {\n        ...state,\n        pager: action.pager,\n        articles: action.payload[1].articles,\n        articlesCount: action.payload[1].articlesCount,\n        currentPage: 0\n      };\n    case PROFILE_PAGE_UNLOADED:\n    case PROFILE_FAVORITES_PAGE_UNLOADED:\n      return {};\n    default:\n      return state;\n  }\n};\n"
  },
  {
    "path": "src/reducers/auth.js",
    "content": "import {\n  LOGIN,\n  REGISTER,\n  LOGIN_PAGE_UNLOADED,\n  REGISTER_PAGE_UNLOADED,\n  ASYNC_START,\n  UPDATE_FIELD_AUTH\n} from '../constants/actionTypes';\n\nexport default (state = {}, action) => {\n  switch (action.type) {\n    case LOGIN:\n    case REGISTER:\n      return {\n        ...state,\n        inProgress: false,\n        errors: action.error ? action.payload.errors : null\n      };\n    case LOGIN_PAGE_UNLOADED:\n    case REGISTER_PAGE_UNLOADED:\n      return {};\n    case ASYNC_START:\n      if (action.subtype === LOGIN || action.subtype === REGISTER) {\n        return { ...state, inProgress: true };\n      }\n      break;\n    case UPDATE_FIELD_AUTH:\n      return { ...state, [action.key]: action.value };\n    default:\n      return state;\n  }\n\n  return state;\n};\n"
  },
  {
    "path": "src/reducers/common.js",
    "content": "import {\n  APP_LOAD,\n  REDIRECT,\n  LOGOUT,\n  ARTICLE_SUBMITTED,\n  SETTINGS_SAVED,\n  LOGIN,\n  REGISTER,\n  DELETE_ARTICLE,\n  ARTICLE_PAGE_UNLOADED,\n  EDITOR_PAGE_UNLOADED,\n  HOME_PAGE_UNLOADED,\n  PROFILE_PAGE_UNLOADED,\n  PROFILE_FAVORITES_PAGE_UNLOADED,\n  SETTINGS_PAGE_UNLOADED,\n  LOGIN_PAGE_UNLOADED,\n  REGISTER_PAGE_UNLOADED\n} from '../constants/actionTypes';\n\nconst defaultState = {\n  appName: 'Conduit',\n  token: null,\n  viewChangeCounter: 0\n};\n\nexport default (state = defaultState, action) => {\n  switch (action.type) {\n    case APP_LOAD:\n      return {\n        ...state,\n        token: action.token || null,\n        appLoaded: true,\n        currentUser: action.payload ? action.payload.user : null\n      };\n    case REDIRECT:\n      return { ...state, redirectTo: null };\n    case LOGOUT:\n      return { ...state, redirectTo: '/', token: null, currentUser: null };\n    case ARTICLE_SUBMITTED:\n      const redirectUrl = `/article/${action.payload.article.slug}`;\n      return { ...state, redirectTo: redirectUrl };\n    case SETTINGS_SAVED:\n      return {\n        ...state,\n        redirectTo: action.error ? null : '/',\n        currentUser: action.error ? null : action.payload.user\n      };\n    case LOGIN:\n    case REGISTER:\n      return {\n        ...state,\n        redirectTo: action.error ? null : '/',\n        token: action.error ? null : action.payload.user.token,\n        currentUser: action.error ? null : action.payload.user\n      };\n    case DELETE_ARTICLE:\n      return { ...state, redirectTo: '/' };\n    case ARTICLE_PAGE_UNLOADED:\n    case EDITOR_PAGE_UNLOADED:\n    case HOME_PAGE_UNLOADED:\n    case PROFILE_PAGE_UNLOADED:\n    case PROFILE_FAVORITES_PAGE_UNLOADED:\n    case SETTINGS_PAGE_UNLOADED:\n    case LOGIN_PAGE_UNLOADED:\n    case REGISTER_PAGE_UNLOADED:\n      return { ...state, viewChangeCounter: state.viewChangeCounter + 1 };\n    default:\n      return state;\n  }\n};\n"
  },
  {
    "path": "src/reducers/editor.js",
    "content": "import {\n  EDITOR_PAGE_LOADED,\n  EDITOR_PAGE_UNLOADED,\n  ARTICLE_SUBMITTED,\n  ASYNC_START,\n  ADD_TAG,\n  REMOVE_TAG,\n  UPDATE_FIELD_EDITOR\n} from '../constants/actionTypes';\n\nexport default (state = {}, action) => {\n  switch (action.type) {\n    case EDITOR_PAGE_LOADED:\n      return {\n        ...state,\n        articleSlug: action.payload ? action.payload.article.slug : '',\n        title: action.payload ? action.payload.article.title : '',\n        description: action.payload ? action.payload.article.description : '',\n        body: action.payload ? action.payload.article.body : '',\n        tagInput: '',\n        tagList: action.payload ? action.payload.article.tagList : []\n      };\n    case EDITOR_PAGE_UNLOADED:\n      return {};\n    case ARTICLE_SUBMITTED:\n      return {\n        ...state,\n        inProgress: null,\n        errors: action.error ? action.payload.errors : null\n      };\n    case ASYNC_START:\n      if (action.subtype === ARTICLE_SUBMITTED) {\n        return { ...state, inProgress: true };\n      }\n      break;\n    case ADD_TAG:\n      return {\n        ...state,\n        tagList: state.tagList.concat([state.tagInput]),\n        tagInput: ''\n      };\n    case REMOVE_TAG:\n      return {\n        ...state,\n        tagList: state.tagList.filter(tag => tag !== action.tag)\n      };\n    case UPDATE_FIELD_EDITOR:\n      return { ...state, [action.key]: action.value };\n    default:\n      return state;\n  }\n\n  return state;\n};\n"
  },
  {
    "path": "src/reducers/home.js",
    "content": "import { HOME_PAGE_LOADED, HOME_PAGE_UNLOADED } from '../constants/actionTypes';\n\nexport default (state = {}, action) => {\n  switch (action.type) {\n    case HOME_PAGE_LOADED:\n      return {\n        ...state,\n        tags: action.payload[0].tags\n      };\n    case HOME_PAGE_UNLOADED:\n      return {};\n    default:\n      return state;\n  }\n};\n"
  },
  {
    "path": "src/reducers/profile.js",
    "content": "import {\n  PROFILE_PAGE_LOADED,\n  PROFILE_PAGE_UNLOADED,\n  FOLLOW_USER,\n  UNFOLLOW_USER\n} from '../constants/actionTypes';\n\nexport default (state = {}, action) => {\n  switch (action.type) {\n    case PROFILE_PAGE_LOADED:\n      return {\n        ...action.payload[0].profile\n      };\n    case PROFILE_PAGE_UNLOADED:\n      return {};\n    case FOLLOW_USER:\n    case UNFOLLOW_USER:\n      return {\n        ...action.payload.profile\n      };\n    default:\n      return state;\n  }\n};\n"
  },
  {
    "path": "src/reducers/settings.js",
    "content": "import {\n  SETTINGS_SAVED,\n  SETTINGS_PAGE_UNLOADED,\n  ASYNC_START\n} from '../constants/actionTypes';\n\nexport default (state = {}, action) => {\n  switch (action.type) {\n    case SETTINGS_SAVED:\n      return {\n        ...state,\n        inProgress: false,\n        errors: action.error ? action.payload.errors : null\n      };\n    case SETTINGS_PAGE_UNLOADED:\n      return {};\n    case ASYNC_START:\n      return {\n        ...state,\n        inProgress: true\n      };\n    default:\n      return state;\n  }\n};\n"
  },
  {
    "path": "src/store.js",
    "content": "import { applyMiddleware, createStore } from 'redux';\nimport { createLogger } from 'redux-logger'\nimport { composeWithDevTools } from 'redux-devtools-extension/developmentOnly';\nimport { promiseMiddleware, localStorageMiddleware } from './middleware';\nimport reducer from './reducer';\n\nimport { routerMiddleware } from 'react-router-redux'\nimport createHistory from 'history/createBrowserHistory';\n\nexport const history = createHistory();\n\n// Build the middleware for intercepting and dispatching navigation actions\nconst myRouterMiddleware = routerMiddleware(history);\n\nconst getMiddleware = () => {\n  if (process.env.NODE_ENV === 'production') {\n    return applyMiddleware(myRouterMiddleware, promiseMiddleware, localStorageMiddleware);\n  } else {\n    // Enable additional logging in non-production environments.\n    return applyMiddleware(myRouterMiddleware, promiseMiddleware, localStorageMiddleware, createLogger())\n  }\n};\n\nexport const store = createStore(\n  reducer, composeWithDevTools(getMiddleware()));\n"
  }
]