animate add todo/filter selector

This commit is contained in:
2018-07-13 22:38:13 +03:00
parent 5cfb9673b0
commit e3e1efa521
14 changed files with 284 additions and 114 deletions

40
app.js
View File

@@ -3,12 +3,12 @@ const express = require('express');
const bodyParser = require('body-parser'); const bodyParser = require('body-parser');
const morgan = require('morgan'); const morgan = require('morgan');
const cors = require('cors'); const cors = require('cors');
const config = require('./config');
const db = require('./config/db');
const path = require('path'); const path = require('path');
const hsts = require('hsts'); const hsts = require('hsts');
const compression = require('compression'); const compression = require('compression');
const { redirectToHTTPS } = require('express-http-to-https'); const { redirectToHTTPS } = require('express-http-to-https');
const db = require('./config/db');
const config = require('./config');
require('./models/TodoList'); require('./models/TodoList');
require('./models/User'); require('./models/User');
@@ -74,22 +74,26 @@ app.use((req, res) => {
// handle errors // handle errors
app.use((error, req, res, next) => { app.use((error, req, res, next) => {
switch (error.name) { if (error.code) {
case 'ValidationError': res.status(error.code);
case 'MissingPasswordError': } else {
case 'BadRequest': switch (error.name) {
case 'BadRequestError': case 'ValidationError':
res.status(400); case 'MissingPasswordError':
break; case 'BadRequest':
case 'AuthenticationError': case 'BadRequestError':
case 'UnauthorizedError': res.status(400);
res.status(401); break;
break; case 'AuthenticationError':
case 'NotFound': case 'UnauthorizedError':
res.status(404); res.status(401);
break; break;
default: case 'NotFound':
res.status(500); res.status(404);
break;
default:
res.status(500);
}
} }
res.json({ success: false, error }); res.json({ success: false, error });
if ( if (

View File

@@ -4,6 +4,7 @@ class NotFoundError extends Error {
Error.captureStackTrace(this, NotFoundError); Error.captureStackTrace(this, NotFoundError);
this.name = 'NotFound'; this.name = 'NotFound';
this.text = text; this.text = text;
this.code = 404;
} }
} }
@@ -13,6 +14,7 @@ class BadRequestError extends Error {
Error.captureStackTrace(this, NotFoundError); Error.captureStackTrace(this, NotFoundError);
this.name = 'BadRequest'; this.name = 'BadRequest';
this.text = text; this.text = text;
this.code = 400;
} }
} }

146
package-lock.json generated
View File

@@ -1688,6 +1688,94 @@
"typedarray": "^0.0.6" "typedarray": "^0.0.6"
} }
}, },
"concurrently": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-3.6.0.tgz",
"integrity": "sha512-6XiIYtYzmGEccNZFkih5JOH92jLA4ulZArAYy5j1uDSdrPLB3KzdE8GW7t2fHPcg9ry2+5LP9IEYzXzxw9lFdA==",
"dev": true,
"requires": {
"chalk": "^2.4.1",
"commander": "2.6.0",
"date-fns": "^1.23.0",
"lodash": "^4.5.1",
"read-pkg": "^3.0.0",
"rx": "2.3.24",
"spawn-command": "^0.0.2-1",
"supports-color": "^3.2.3",
"tree-kill": "^1.1.0"
},
"dependencies": {
"commander": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz",
"integrity": "sha1-nfflL7Kgyw+4kFjugMMQQiXzfh0=",
"dev": true
},
"has-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
"integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
"dev": true
},
"load-json-file": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
"integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
"dev": true,
"requires": {
"graceful-fs": "^4.1.2",
"parse-json": "^4.0.0",
"pify": "^3.0.0",
"strip-bom": "^3.0.0"
}
},
"parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
"dev": true,
"requires": {
"error-ex": "^1.3.1",
"json-parse-better-errors": "^1.0.1"
}
},
"path-type": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
"integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
"dev": true,
"requires": {
"pify": "^3.0.0"
}
},
"pify": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
"dev": true
},
"read-pkg": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
"integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
"dev": true,
"requires": {
"load-json-file": "^4.0.0",
"normalize-package-data": "^2.3.2",
"path-type": "^3.0.0"
}
},
"supports-color": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
"integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
"dev": true,
"requires": {
"has-flag": "^1.0.0"
}
}
}
},
"configstore": { "configstore": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz",
@@ -1853,6 +1941,12 @@
"whatwg-url": "^6.4.0" "whatwg-url": "^6.4.0"
} }
}, },
"date-fns": {
"version": "1.29.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.29.0.tgz",
"integrity": "sha512-lbTXWZ6M20cWH8N9S6afb0SBm6tMk+uUg6z3MqHPKE9atmsY3kJkTm8vKe93izJ2B2+q5MV990sM2CHgtAZaOw==",
"dev": true
},
"debug": { "debug": {
"version": "2.6.9", "version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -3514,14 +3608,12 @@
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
"concat-map": "0.0.1" "concat-map": "0.0.1"
@@ -3536,20 +3628,17 @@
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"console-control-strings": { "console-control-strings": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
@@ -3666,8 +3755,7 @@
"inherits": { "inherits": {
"version": "2.0.3", "version": "2.0.3",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
@@ -3679,7 +3767,6 @@
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"number-is-nan": "^1.0.0" "number-is-nan": "^1.0.0"
} }
@@ -3694,7 +3781,6 @@
"version": "3.0.4", "version": "3.0.4",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
} }
@@ -3702,14 +3788,12 @@
"minimist": { "minimist": {
"version": "0.0.8", "version": "0.0.8",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"minipass": { "minipass": {
"version": "2.2.4", "version": "2.2.4",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"safe-buffer": "^5.1.1", "safe-buffer": "^5.1.1",
"yallist": "^3.0.0" "yallist": "^3.0.0"
@@ -3728,7 +3812,6 @@
"version": "0.5.1", "version": "0.5.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "0.0.8"
} }
@@ -3809,8 +3892,7 @@
"number-is-nan": { "number-is-nan": {
"version": "1.0.1", "version": "1.0.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
@@ -3822,7 +3904,6 @@
"version": "1.4.0", "version": "1.4.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"wrappy": "1" "wrappy": "1"
} }
@@ -3944,7 +4025,6 @@
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"code-point-at": "^1.0.0", "code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0", "is-fullwidth-code-point": "^1.0.0",
@@ -5482,6 +5562,12 @@
"integrity": "sha1-5CGiqOINawgZ3yiQj3glJrlt0f4=", "integrity": "sha1-5CGiqOINawgZ3yiQj3glJrlt0f4=",
"dev": true "dev": true
}, },
"json-parse-better-errors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
"dev": true
},
"json-schema": { "json-schema": {
"version": "0.2.3", "version": "0.2.3",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
@@ -7593,6 +7679,12 @@
"is-promise": "^2.1.0" "is-promise": "^2.1.0"
} }
}, },
"rx": {
"version": "2.3.24",
"resolved": "https://registry.npmjs.org/rx/-/rx-2.3.24.tgz",
"integrity": "sha1-FPlQpCF9fjXapxu8vljv9o6ksrc=",
"dev": true
},
"rx-lite": { "rx-lite": {
"version": "4.0.8", "version": "4.0.8",
"resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz",
@@ -8001,6 +8093,12 @@
"integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
"dev": true "dev": true
}, },
"spawn-command": {
"version": "0.0.2-1",
"resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
"integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=",
"dev": true
},
"spdx-correct": { "spdx-correct": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz",
@@ -8527,6 +8625,12 @@
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
}, },
"tree-kill": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.0.tgz",
"integrity": "sha512-DlX6dR0lOIRDFxI0mjL9IYg6OTncLm/Zt+JiBhE5OlFcAR8yc9S7FFXU9so0oda47frdM/JFsk7UjNt9vscKcg==",
"dev": true
},
"trim-right": { "trim-right": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",

View File

@@ -6,8 +6,10 @@
"main": "app.js", "main": "app.js",
"scripts": { "scripts": {
"start": "node ./app.js", "start": "node ./app.js",
"debug": "cross-env NODE_ENV=development npx nodemon --inspect ./app.js", "dev": "npx concurrently \"npm run server\" \"npm run client\" ",
"test": "cross-env NODE_ENV=test jest", "client": "cd react && npm start",
"server": "npx cross-env NODE_ENV=development npx nodemon --inspect ./app.js",
"test": "npx cross-env NODE_ENV=test jest",
"heroku-postbuild": "cd react && npm install && npm run build" "heroku-postbuild": "cd react && npm install && npm run build"
}, },
"cacheDirectories": [ "cacheDirectories": [
@@ -38,6 +40,7 @@
"passport-local-mongoose": "^5.0.1" "passport-local-mongoose": "^5.0.1"
}, },
"devDependencies": { "devDependencies": {
"concurrently": "^3.6.0",
"cross-env": "^5.2.0", "cross-env": "^5.2.0",
"eslint": "^5.1.0", "eslint": "^5.1.0",
"eslint-config-airbnb-base": "^13.0.0", "eslint-config-airbnb-base": "^13.0.0",

View File

@@ -53,7 +53,6 @@
} }
#inputs { #inputs {
transition: 0.4s ease-in-out;
box-shadow: 0 2px 7px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 7px rgba(0, 0, 0, 0.1);
display: flex; display: flex;
height: 2.5rem; height: 2.5rem;

View File

@@ -6,7 +6,7 @@ import CssBaseline from '@material-ui/core/CssBaseline';
import './Container.css'; import './Container.css';
import './App.css'; import './App.css';
import TodosContainer from '../containers/TodosContainer'; import MainViewContainer from '../containers/MainViewContainer';
import LoginForm from './user/LoginForm'; import LoginForm from './user/LoginForm';
import SignupForm from './user/SignupForm'; import SignupForm from './user/SignupForm';
@@ -22,7 +22,7 @@ export default class App extends React.PureComponent {
<CssBaseline /> <CssBaseline />
<Router> <Router>
<div id="container"> <div id="container">
<Route exact path="/" component={TodosContainer} /> <Route exact path="/" component={MainViewContainer} />
<Route path="/login" component={LoginForm} /> <Route path="/login" component={LoginForm} />
<Route path="/signup" component={SignupForm} /> <Route path="/signup" component={SignupForm} />
</div> </div>

View File

@@ -2,9 +2,9 @@ import React from 'react';
import FilterLink from '../containers/FilterLink'; import FilterLink from '../containers/FilterLink';
import { VisibilityFilters } from '../actions/defs'; import { VisibilityFilters } from '../actions/defs';
function Filters() { function Filters(styles) {
return ( return (
<div id="filters"> <div style={styles} id="filters">
<FilterLink filter={VisibilityFilters.SHOW_ALL}>all</FilterLink> <FilterLink filter={VisibilityFilters.SHOW_ALL}>all</FilterLink>
<FilterLink filter={VisibilityFilters.SHOW_ACTIVE}>active</FilterLink> <FilterLink filter={VisibilityFilters.SHOW_ACTIVE}>active</FilterLink>
<FilterLink filter={VisibilityFilters.SHOW_COMPLETED}> <FilterLink filter={VisibilityFilters.SHOW_COMPLETED}>

View File

@@ -3,18 +3,18 @@ import PropTypes from 'prop-types';
import { Button } from '@material-ui/core'; import { Button } from '@material-ui/core';
import AddIcon from '@material-ui/icons/Add'; import AddIcon from '@material-ui/icons/Add';
function Input(props) { function Input({ onClick, styles }) {
let input; let input;
function submit() { function submit() {
if (input.value.trim() !== '') { if (input.value.trim() !== '') {
props.onClick(input.value); onClick(input.value);
} }
input.value = ''; input.value = '';
} }
return ( return (
<div id="inputs"> <div style={styles} id="inputs">
<input <input
ref={node => { ref={node => {
input = node; input = node;
@@ -36,6 +36,7 @@ function Input(props) {
} }
Input.propTypes = { Input.propTypes = {
styles: PropTypes.any.isRequired,
onClick: PropTypes.func.isRequired, onClick: PropTypes.func.isRequired,
}; };

View File

@@ -0,0 +1,21 @@
import React from 'react';
import PropTypes from 'prop-types';
import TodosContainer from '../containers/TodosContainer';
export default class MainView extends React.PureComponent {
componentDidUpdate() {
const { user, history } = this.props;
if (!user.user && !user.dirty) {
history.replace('/login');
}
}
render() {
return <TodosContainer />;
}
}
MainView.propTypes = {
user: PropTypes.any.isRequired,
history: PropTypes.any.isRequired,
};

View File

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { Select, MenuItem } from '@material-ui/core'; import { Select, MenuItem } from '@material-ui/core';
import AddIcon from '@material-ui/icons/Add'; import AddIcon from '@material-ui/icons/Add';
import CheckIcon from '@material-ui/icons/Check'; import CheckIcon from '@material-ui/icons/Check';
import { Spring, animated } from 'react-spring'; import { Transition, animated } from 'react-spring';
import './Selector.css'; import './Selector.css';
@@ -28,65 +28,83 @@ export default function Selector({
if (creating) { if (creating) {
let input = null; let input = null;
return ( return (
<div id="listselector" className="list--input"> <Transition
<input native
ref={node => { from={{ opacity: 0, maxHeight: 0 }}
input = node; enter={{ opacity: 1, maxHeight: 64 }}
}} leave={{ opacity: 0, maxHeight: 0 }}
id="input" >
type="text" {styles => (
onKeyPress={e => { <animated.div
if (e.key === 'Enter') { style={styles}
addList(input.value); id="listselector"
} className="list--input"
}} >
/> <input
<Spring native from={{ opacity: 0 }} to={{ opacity: 1 }}> ref={node => {
{styles => ( input = node;
}}
id="input"
type="text"
onKeyPress={e => {
if (e.key === 'Enter') {
addList(input.value);
}
}}
/>
<animated.button <animated.button
style={{ ...button, ...styles }} style={{ ...button, ...styles }}
onClick={() => input.value.trim() && addList(input.value)} onClick={() => input.value.trim() && addList(input.value)}
> >
<AddIcon style={icon} /> <AddIcon style={icon} />
</animated.button> </animated.button>
)} </animated.div>
</Spring> )}
</div> </Transition>
); );
} }
if (editing) { if (editing) {
let input = null; let input = null;
return ( return (
<div id="listselector" className="list--input"> <Transition
<input native
ref={node => { from={{ opacity: 0, maxHeight: 0 }}
input = node; enter={{ opacity: 1, maxHeight: 64 }}
}} leave={{ opacity: 0, maxHeight: 0 }}
defaultValue={lists.lists[list].name} >
id="input" {styles => (
type="text" <animated.div
onKeyPress={e => { style={styles}
if (e.key === 'Enter') { id="listselector"
editList(input.value); className="list--input"
} >
}} <input
/> ref={node => {
<Spring native from={{ opacity: 0 }} to={{ opacity: 1 }}> input = node;
{styles => ( }}
defaultValue={lists.lists[list].name}
id="input"
type="text"
onKeyPress={e => {
if (e.key === 'Enter') {
editList(input.value);
}
}}
/>
<animated.button <animated.button
style={{ ...button, ...styles }} style={{ ...button }}
onClick={() => input.value.trim() && editList(input.value)} onClick={() => input.value.trim() && editList(input.value)}
> >
<CheckIcon style={icon} /> <CheckIcon style={icon} />
</animated.button> </animated.button>
)} </animated.div>
</Spring> )}
</div> </Transition>
); );
} }
if (list) { if (list) {
return ( return (
<div id="listselector"> <animated.div id="listselector">
<Select <Select
style={{ fontSize: '1.5rem', width: '100%' }} style={{ fontSize: '1.5rem', width: '100%' }}
value={list} value={list}
@@ -98,7 +116,7 @@ export default function Selector({
</MenuItem> </MenuItem>
))} ))}
</Select> </Select>
</div> </animated.div>
); );
} }
return null; return null;

View File

@@ -1,32 +1,35 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Transition } from 'react-spring';
import InputContainer from '../containers/InputContainer'; import InputContainer from '../containers/InputContainer';
import TodoListContainer from '../containers/TodoListContainer'; import TodoListContainer from '../containers/TodoListContainer';
import Header from './Header'; import Header from './Header';
import Filters from './Filters'; import Filters from './Filters';
export default class Todos extends React.PureComponent { export default function Todos({ list }) {
componentDidUpdate() { return (
const { user, history } = this.props; <div id="todos">
if (!user.user && !user.dirty) { <Header />
history.replace('/login'); <Transition
} from={{ opacity: 0, maxHeight: 0 }}
} enter={{ opacity: 1, maxHeight: 38 }}
leave={{ opacity: 0, maxHeight: 0 }}
render() { >
return ( {list && (styles => <InputContainer styles={styles} />)}
<div id="todos"> </Transition>
<Header /> <TodoListContainer />
<InputContainer /> <Transition
<TodoListContainer /> from={{ opacity: 0, maxHeight: 0 }}
<Filters /> enter={{ opacity: 1, maxHeight: 32 }}
</div> leave={{ opacity: 0, maxHeight: 0 }}
); >
} {list && Filters}
</Transition>
</div>
);
} }
Todos.propTypes = { Todos.propTypes = {
history: PropTypes.object.isRequired, list: PropTypes.bool.isRequired,
user: PropTypes.object.isRequired,
}; };

View File

@@ -3,6 +3,10 @@ import { connect } from 'react-redux';
import Input from '../components/Input'; import Input from '../components/Input';
import { addTodo } from '../actions/todos'; import { addTodo } from '../actions/todos';
function mapStateToProps(state, ownProps) {
return { ...ownProps };
}
function mapDispatchToProps(dispatch) { function mapDispatchToProps(dispatch) {
return { return {
onClick: text => dispatch(addTodo(text)), onClick: text => dispatch(addTodo(text)),
@@ -10,6 +14,6 @@ function mapDispatchToProps(dispatch) {
} }
export default connect( export default connect(
null, mapStateToProps,
mapDispatchToProps, mapDispatchToProps,
)(Input); )(Input);

View File

@@ -0,0 +1,12 @@
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import MainView from '../components/MainView';
function mapStateToProps(state) {
return {
user: state.user,
};
}
export default withRouter(connect(mapStateToProps)(MainView));

View File

@@ -1,12 +1,11 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import Todos from '../components/Todos'; import Todos from '../components/Todos';
function mapStateToProps(state) { function mapStateToProps(state) {
return { return {
user: state.user, list: Boolean(state.lists.list),
}; };
} }
export default withRouter(connect(mapStateToProps)(Todos)); export default connect(mapStateToProps)(Todos);