mirror of
https://github.com/usatiuk/ustk-todolist.git
synced 2025-10-28 23:57:49 +01:00
add, remove lists
This commit is contained in:
5449
package-lock.json
generated
5449
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -105,7 +105,7 @@ li:first-child .item {
|
||||
box-shadow: inset -3px 0 3px -3px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.item {
|
||||
.todo {
|
||||
box-sizing: border-box;
|
||||
word-wrap: break-word;
|
||||
max-width: 100%;
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'normalize.css';
|
||||
import './App.css';
|
||||
|
||||
import InputContainer from './containers/InputContainer';
|
||||
import ItemsContainer from './containers/ItemsContainer';
|
||||
import TodosContainer from './containers/TodosContainer';
|
||||
import Header from './components/Header';
|
||||
|
||||
export default function App() {
|
||||
@@ -11,7 +11,7 @@ export default function App() {
|
||||
<div id="container">
|
||||
<Header />
|
||||
<InputContainer />
|
||||
<ItemsContainer />
|
||||
<TodosContainer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
180
src/actions.js
180
src/actions.js
@@ -1,8 +1,9 @@
|
||||
const API_ROOT = 'http://localhost:4000';
|
||||
|
||||
export const ADD_ITEM = 'ADD_ITEM';
|
||||
export const REMOVE_ITEM = 'REMOVE_ITEM';
|
||||
export const TOGGLE_ITEM = 'TOGGLE_ITEM';
|
||||
export const ADD_TODO = 'ADD_TODO';
|
||||
export const REMOVE_TODO = 'REMOVE_TODO';
|
||||
export const TOGGLE_TODO = 'TOGGLE_TODO';
|
||||
export const EDIT_TODO = 'EDIT_TODO';
|
||||
export const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER';
|
||||
export const RECIEVE_TODOS = 'RECIEVE_TODOS';
|
||||
export const REQUEST_TODOS = 'REQUEST_TODOS';
|
||||
@@ -15,8 +16,8 @@ export const VisibilityFilters = {
|
||||
SHOW_ACTIVE: 'SHOW_ACTIVE',
|
||||
};
|
||||
|
||||
function toggleItemInList(list, id) {
|
||||
return { type: TOGGLE_ITEM, list, id };
|
||||
function toggleTodoInList(id) {
|
||||
return { type: TOGGLE_TODO, id };
|
||||
}
|
||||
|
||||
export function setVisibilityFilter(filter) {
|
||||
@@ -29,42 +30,45 @@ function requestTodos(list) {
|
||||
function recieveTodos(list, todos) {
|
||||
return { type: RECIEVE_TODOS, list, todos };
|
||||
}
|
||||
function invalidateTodos(list) {
|
||||
return { type: INVALIDATE_TODOS, list };
|
||||
function invalidateTodos() {
|
||||
return { type: INVALIDATE_TODOS };
|
||||
}
|
||||
function validateTodos(list) {
|
||||
return { type: VALIDATE_TODOS, list };
|
||||
function validateTodos() {
|
||||
return { type: VALIDATE_TODOS };
|
||||
}
|
||||
|
||||
function addTodoToList(list, todo) {
|
||||
return { type: ADD_ITEM, list, todo };
|
||||
function addTodoToList(todo) {
|
||||
return { type: ADD_TODO, todo };
|
||||
}
|
||||
|
||||
export function addItem(list, text) {
|
||||
export function addTodo(text) {
|
||||
return async (dispatch, getState) => {
|
||||
dispatch(invalidateTodos(list));
|
||||
const state = getState();
|
||||
const response = await fetch(`${API_ROOT}/lists/${state.lists.lists[list].slug}/todos`, {
|
||||
body: JSON.stringify({ text }),
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
method: 'POST',
|
||||
});
|
||||
const json = await response.json();
|
||||
const todo = json.data;
|
||||
dispatch(addTodoToList(list, todo));
|
||||
dispatch(validateTodos(list));
|
||||
const { list } = state.lists;
|
||||
if (list) {
|
||||
dispatch(invalidateTodos());
|
||||
const response = await fetch(`${API_ROOT}/lists/${list}/todos`, {
|
||||
body: JSON.stringify({ text }),
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
method: 'POST',
|
||||
});
|
||||
const json = await response.json();
|
||||
const todo = json.data;
|
||||
dispatch(addTodoToList(todo));
|
||||
dispatch(validateTodos());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function removeTodoFromList(list, id) {
|
||||
return { type: REMOVE_ITEM, list, id };
|
||||
function removeTodoFromList(id) {
|
||||
return { type: REMOVE_TODO, id };
|
||||
}
|
||||
|
||||
export function removeItem(list, id) {
|
||||
export function removeTodo(id) {
|
||||
return async (dispatch) => {
|
||||
dispatch(invalidateTodos(list));
|
||||
dispatch(invalidateTodos());
|
||||
const response = await fetch(`${API_ROOT}/todos/${id}`, {
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
@@ -73,17 +77,17 @@ export function removeItem(list, id) {
|
||||
});
|
||||
const json = await response.json();
|
||||
if (json.success) {
|
||||
dispatch(removeTodoFromList(list, id));
|
||||
dispatch(removeTodoFromList(id));
|
||||
}
|
||||
dispatch(validateTodos(list));
|
||||
dispatch(validateTodos());
|
||||
};
|
||||
}
|
||||
|
||||
export function toggleItem(list, id) {
|
||||
export function toggleTodo(id) {
|
||||
return async (dispatch, getState) => {
|
||||
dispatch(invalidateTodos(list));
|
||||
dispatch(invalidateTodos());
|
||||
const state = getState();
|
||||
const listObj = state.lists.lists[list];
|
||||
const listObj = state.lists.lists[state.lists.list];
|
||||
const todoObj = listObj.todos.find(todo => todo.id === id);
|
||||
const completed = !todoObj.completed;
|
||||
const response = await fetch(`${API_ROOT}/todos/${id}`, {
|
||||
@@ -95,16 +99,39 @@ export function toggleItem(list, id) {
|
||||
});
|
||||
const json = await response.json();
|
||||
if (json.success) {
|
||||
dispatch(toggleItemInList(list, id));
|
||||
dispatch(toggleTodoInList(id));
|
||||
}
|
||||
dispatch(validateTodos(list));
|
||||
dispatch(validateTodos());
|
||||
};
|
||||
}
|
||||
|
||||
function editTodoInList(id, todo) {
|
||||
return { type: EDIT_TODO, id, todo };
|
||||
}
|
||||
|
||||
export function editTodo(id, text) {
|
||||
return async (dispatch) => {
|
||||
dispatch(invalidateTodos());
|
||||
const response = await fetch(`${API_ROOT}/todos/${id}`, {
|
||||
body: JSON.stringify({ text }),
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
method: 'PATCH',
|
||||
});
|
||||
const json = await response.json();
|
||||
if (json.success) {
|
||||
const todo = json.data;
|
||||
dispatch(editTodoInList(id, todo));
|
||||
}
|
||||
dispatch(validateTodos());
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchTodos(list) {
|
||||
return async (dispatch) => {
|
||||
dispatch(requestTodos(list));
|
||||
const response = await fetch(`${API_ROOT}/todos`);
|
||||
const response = await fetch(`${API_ROOT}/lists/${list.id}/todos`);
|
||||
const json = await response.json();
|
||||
const todos = json.data;
|
||||
dispatch(recieveTodos(list, todos));
|
||||
@@ -113,6 +140,7 @@ export function fetchTodos(list) {
|
||||
|
||||
export const ADD_LIST = 'ADD_LIST';
|
||||
export const REMOVE_LIST = 'REMOVE_LIST';
|
||||
export const EDIT_LIST = 'EDIT_LIST';
|
||||
export const RECIEVE_LISTS = 'RECIEVE_LISTS';
|
||||
export const REQUEST_LISTS = 'REQUEST_LISTS';
|
||||
export const INVALIDATE_LISTS = 'INVALIDATE_LISTS';
|
||||
@@ -125,6 +153,7 @@ function requestLists() {
|
||||
function recieveLists(lists) {
|
||||
return { type: RECIEVE_LISTS, lists };
|
||||
}
|
||||
|
||||
function invalidateLists() {
|
||||
return { type: INVALIDATE_LISTS };
|
||||
}
|
||||
@@ -135,11 +164,83 @@ export function changeList(list) {
|
||||
return { type: CHANGE_LIST, list };
|
||||
}
|
||||
|
||||
function addListToState(list) {
|
||||
return { type: ADD_LIST, list };
|
||||
}
|
||||
|
||||
export function addList(name) {
|
||||
return async (dispatch) => {
|
||||
dispatch(invalidateLists());
|
||||
const response = await fetch(`${API_ROOT}/lists`, {
|
||||
body: JSON.stringify({ name }),
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
method: 'POST',
|
||||
});
|
||||
const json = await response.json();
|
||||
const list = json.data;
|
||||
dispatch(addListToState(list));
|
||||
dispatch(changeList(list.id));
|
||||
dispatch(validateLists());
|
||||
};
|
||||
}
|
||||
|
||||
function removeListFromState(id) {
|
||||
return { type: REMOVE_LIST, id };
|
||||
}
|
||||
|
||||
export function removeList(id) {
|
||||
return async (dispatch, getState) => {
|
||||
dispatch(invalidateLists());
|
||||
const response = await fetch(`${API_ROOT}/lists/${id}`, {
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
method: 'DELETE',
|
||||
});
|
||||
const json = await response.json();
|
||||
if (json.success) {
|
||||
dispatch(removeListFromState(id));
|
||||
const state = getState();
|
||||
const list = state.lists.lists[Object.keys(state.lists.lists)[0]]
|
||||
? state.lists.lists[Object.keys(state.lists.lists)[0]].id
|
||||
: '';
|
||||
dispatch(changeList(list));
|
||||
}
|
||||
dispatch(validateLists());
|
||||
};
|
||||
}
|
||||
|
||||
function editListInState(id, list) {
|
||||
return { type: EDIT_LIST, id, list };
|
||||
}
|
||||
|
||||
export function editList(id, name) {
|
||||
return async (dispatch) => {
|
||||
dispatch(invalidateLists());
|
||||
const response = await fetch(`${API_ROOT}/lists/${id}`, {
|
||||
body: JSON.stringify({ name }),
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
method: 'PATCH',
|
||||
});
|
||||
const json = await response.json();
|
||||
if (json.success) {
|
||||
const list = json.data;
|
||||
dispatch(editListInState(id, list));
|
||||
}
|
||||
dispatch(validateLists());
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchLists() {
|
||||
return async (dispatch) => {
|
||||
dispatch(requestLists());
|
||||
|
||||
try {
|
||||
const listsJson = localStorage.getItem('lists');
|
||||
const listsJson = localStorage.getTodo('lists');
|
||||
const listsObj = JSON.parse(listsJson);
|
||||
dispatch(recieveLists(listsObj));
|
||||
dispatch(changeList(listsObj[Object.keys(listsObj)[0]].id));
|
||||
@@ -155,8 +256,11 @@ export function fetchLists() {
|
||||
newObj[list.id] = list;
|
||||
return newObj;
|
||||
}, {});
|
||||
|
||||
dispatch(recieveLists(listsObj));
|
||||
dispatch(changeList(listsObj[Object.keys(listsObj)[0]].id));
|
||||
if (lists.length !== 0) {
|
||||
dispatch(changeList(listsObj[Object.keys(listsObj)[0]].id));
|
||||
}
|
||||
localStorage.setItem('lists', JSON.stringify(listsObj));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React from 'react';
|
||||
import FilterLink from '../containers/FilterLink';
|
||||
import ListSelector from '../containers/ListSelector';
|
||||
import { VisibilityFilters } from '../actions';
|
||||
import ListsContainer from '../containers/ListsContainer';
|
||||
|
||||
export default function Header() {
|
||||
return (
|
||||
<div className="header">
|
||||
<div id="listselector">
|
||||
<ListSelector />
|
||||
<ListsContainer />
|
||||
</div>
|
||||
<div className="filters">
|
||||
<FilterLink filter={VisibilityFilters.SHOW_ALL}>all</FilterLink>
|
||||
|
||||
21
src/components/ListActions.js
Normal file
21
src/components/ListActions.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export default function ListActions({ addList, removeList, list }) {
|
||||
return (
|
||||
<div>
|
||||
<button onClick={() => addList(prompt('List name?'))}>Add</button>
|
||||
<button onClick={() => removeList(list)}>Remove</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
ListActions.defaultProps = {
|
||||
list: '',
|
||||
};
|
||||
|
||||
ListActions.propTypes = {
|
||||
addList: PropTypes.func.isRequired,
|
||||
removeList: PropTypes.func.isRequired,
|
||||
list: PropTypes.string,
|
||||
};
|
||||
30
src/components/Lists.js
Normal file
30
src/components/Lists.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Selector from './Selector';
|
||||
import ListActions from './ListActions';
|
||||
|
||||
export default function Lists({
|
||||
list, lists, onChange, addList, removeList,
|
||||
}) {
|
||||
const selectorProps = { list, lists, onChange };
|
||||
const actionsProps = { addList, removeList, list };
|
||||
return (
|
||||
<div>
|
||||
<Selector {...selectorProps} /> <ListActions {...actionsProps} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Lists.defaultProps = {
|
||||
list: '',
|
||||
};
|
||||
|
||||
Lists.propTypes = {
|
||||
lists: PropTypes.arrayOf(PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
})).isRequired,
|
||||
list: PropTypes.string,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
addList: PropTypes.func.isRequired,
|
||||
removeList: PropTypes.func.isRequired,
|
||||
};
|
||||
@@ -7,20 +7,24 @@ export default function Selector(props) {
|
||||
{list.name}
|
||||
</option>
|
||||
));
|
||||
console.log(props.list);
|
||||
return (
|
||||
<div>
|
||||
<select defaultValue={props.list} onChange={e => props.onChange(e.target.value)}>
|
||||
<select value={props.list} onChange={e => props.onChange(e.target.value)}>
|
||||
{lists}
|
||||
</select>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Selector.defaultProps = {
|
||||
list: '',
|
||||
};
|
||||
|
||||
Selector.propTypes = {
|
||||
lists: PropTypes.arrayOf(PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
slug: PropTypes.string.isRequired,
|
||||
})).isRequired,
|
||||
list: PropTypes.string.isRequired,
|
||||
list: PropTypes.string,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import * as React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
class Item extends React.Component {
|
||||
class Todo extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@@ -29,9 +29,9 @@ class Item extends React.Component {
|
||||
deleteClasses.push('disabled');
|
||||
}
|
||||
|
||||
const itemClasses = ['item'];
|
||||
if (this.props.item.completed) {
|
||||
itemClasses.push('done');
|
||||
const todoClasses = ['todo'];
|
||||
if (this.props.todo.completed) {
|
||||
todoClasses.push('done');
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -44,16 +44,16 @@ class Item extends React.Component {
|
||||
<div className={deleteClasses.join(' ')} onClick={this.props.handleDelete}>
|
||||
<FontAwesomeIcon icon={faTrash} />
|
||||
</div>
|
||||
<div className={itemClasses.join(' ')} onClick={this.props.onClick}>
|
||||
{this.props.item.text}
|
||||
<div className={todoClasses.join(' ')} onClick={this.props.onClick}>
|
||||
{this.props.todo.text}
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Item.propTypes = {
|
||||
item: PropTypes.shape({
|
||||
Todo.propTypes = {
|
||||
todo: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
text: PropTypes.string.isRequired,
|
||||
completed: PropTypes.bool.isRequired,
|
||||
@@ -62,4 +62,4 @@ Item.propTypes = {
|
||||
onClick: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default Item;
|
||||
export default Todo;
|
||||
@@ -1,26 +1,26 @@
|
||||
import * as React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import Item from './Item';
|
||||
import Todo from './Todo';
|
||||
|
||||
export default function ItemsContainer(props) {
|
||||
const items = props.items.map(item => (
|
||||
<Item
|
||||
key={item.id}
|
||||
item={item}
|
||||
onClick={() => props.onItemClick(item.list, item.id)}
|
||||
handleDelete={() => props.handleDelete(item.list, item.id)}
|
||||
export default function TodosContainer(props) {
|
||||
const todos = props.todos.map(todo => (
|
||||
<Todo
|
||||
key={todo.id}
|
||||
todo={todo}
|
||||
onClick={() => props.onTodoClick(todo.id)}
|
||||
handleDelete={() => props.handleDelete(todo.id)}
|
||||
/>
|
||||
));
|
||||
return <ul id="list">{items}</ul>;
|
||||
return <ul id="list">{todos}</ul>;
|
||||
}
|
||||
|
||||
ItemsContainer.propTypes = {
|
||||
items: PropTypes.arrayOf(PropTypes.shape({
|
||||
TodosContainer.propTypes = {
|
||||
todos: PropTypes.arrayOf(PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
text: PropTypes.string.isRequired,
|
||||
completed: PropTypes.bool.isRequired,
|
||||
})).isRequired,
|
||||
handleDelete: PropTypes.func.isRequired,
|
||||
onItemClick: PropTypes.func.isRequired,
|
||||
onTodoClick: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
@@ -3,49 +3,35 @@ import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import Input from '../components/Input';
|
||||
import { addItem, VisibilityFilters } from '../actions';
|
||||
import { addTodo } from '../actions';
|
||||
import getVisibleTodos from './getVisibleTodos';
|
||||
|
||||
const InputContainer = props => (
|
||||
<Input
|
||||
inputBottomBorder={props.inputBottomBorder}
|
||||
onClick={text => props.dispatch(addItem(props.list, text))}
|
||||
onClick={text => props.dispatch(addTodo(text))}
|
||||
/>
|
||||
);
|
||||
|
||||
function getVisibleItems(items, filter) {
|
||||
switch (filter) {
|
||||
case VisibilityFilters.SHOW_ALL:
|
||||
return items;
|
||||
case VisibilityFilters.SHOW_ACTIVE:
|
||||
return items.filter(item => !item.completed);
|
||||
case VisibilityFilters.SHOW_COMPLETED:
|
||||
return items.filter(item => item.completed);
|
||||
default:
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
InputContainer.propTypes = {
|
||||
list: PropTypes.string.isRequired,
|
||||
inputBottomBorder: PropTypes.bool.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
const { list } = state.lists;
|
||||
if (!list) {
|
||||
try {
|
||||
const listTodos = state.lists.lists[list].todos;
|
||||
return {
|
||||
list,
|
||||
inputBottomBorder: getVisibleTodos(listTodos, state.visibilityFilter).length !== 0,
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
list,
|
||||
inputBottomBorder: false,
|
||||
};
|
||||
}
|
||||
const listItems = state.lists.lists[list].todos;
|
||||
if (!listItems) {
|
||||
return { list, inputBottomBorder: false };
|
||||
}
|
||||
return {
|
||||
list,
|
||||
inputBottomBorder: getVisibleItems(listItems, state.visibilityFilter).length !== 0,
|
||||
};
|
||||
}
|
||||
|
||||
const InputContainerConnected = connect(mapStateToProps)(InputContainer);
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
import { connect } from 'react-redux';
|
||||
import TodoList from '../components/TodoList';
|
||||
import { toggleItem, removeItem, VisibilityFilters } from '../actions';
|
||||
|
||||
function getVisibleItems(items, filter) {
|
||||
switch (filter) {
|
||||
case VisibilityFilters.SHOW_ALL:
|
||||
return items;
|
||||
case VisibilityFilters.SHOW_ACTIVE:
|
||||
return items.filter(item => !item.completed);
|
||||
case VisibilityFilters.SHOW_COMPLETED:
|
||||
return items.filter(item => item.completed);
|
||||
default:
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
const stub = {
|
||||
list: '',
|
||||
items: [],
|
||||
dirty: true,
|
||||
};
|
||||
const { list } = state.lists;
|
||||
const listObj = state.lists.lists[list];
|
||||
if (!list) {
|
||||
return stub;
|
||||
}
|
||||
const listItems = state.lists.lists[list].todos;
|
||||
if (!listItems) {
|
||||
return stub;
|
||||
}
|
||||
return {
|
||||
list,
|
||||
items: getVisibleItems(listItems, state.visibilityFilter),
|
||||
dirty: listObj.dirty,
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
onItemClick: (list, id) => dispatch(toggleItem(list, id)),
|
||||
handleDelete: (list, id) => dispatch(removeItem(list, id)),
|
||||
};
|
||||
}
|
||||
|
||||
const ItemsContainer = connect(mapStateToProps, mapDispatchToProps)(TodoList);
|
||||
|
||||
export default ItemsContainer;
|
||||
@@ -1,16 +1,8 @@
|
||||
import { connect } from 'react-redux';
|
||||
import Selector from '../components/Selector';
|
||||
import { changeList } from '../actions';
|
||||
import Lists from '../components/Lists';
|
||||
import { changeList, removeList, addList } from '../actions';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
const { list } = state.lists;
|
||||
if (!list) {
|
||||
return {
|
||||
lists: [],
|
||||
list: '',
|
||||
dirty: state.lists.dirty,
|
||||
};
|
||||
}
|
||||
return {
|
||||
lists: Object.values(state.lists.lists),
|
||||
list: state.lists.list,
|
||||
@@ -21,7 +13,9 @@ function mapStateToProps(state) {
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
onChange: list => dispatch(changeList(list)),
|
||||
addList: name => dispatch(addList(name)),
|
||||
removeList: id => dispatch(removeList(id)),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Selector);
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Lists);
|
||||
36
src/containers/TodosContainer.js
Normal file
36
src/containers/TodosContainer.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import { connect } from 'react-redux';
|
||||
import TodoList from '../components/TodoList';
|
||||
import { toggleTodo, removeTodo } from '../actions';
|
||||
|
||||
import getVisibleTodos from './getVisibleTodos';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
const { list } = state.lists;
|
||||
try {
|
||||
const listObj = state.lists.lists[list];
|
||||
const listTodos = state.lists.lists[list].todos;
|
||||
|
||||
return {
|
||||
list,
|
||||
todos: getVisibleTodos(listTodos, state.visibilityFilter),
|
||||
dirty: listObj.dirty,
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
list: '',
|
||||
todos: [],
|
||||
dirty: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
onTodoClick: id => dispatch(toggleTodo(id)),
|
||||
handleDelete: id => dispatch(removeTodo(id)),
|
||||
};
|
||||
}
|
||||
|
||||
const TodosContainer = connect(mapStateToProps, mapDispatchToProps)(TodoList);
|
||||
|
||||
export default TodosContainer;
|
||||
14
src/containers/getVisibleTodos.js
Normal file
14
src/containers/getVisibleTodos.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import { VisibilityFilters } from '../actions';
|
||||
|
||||
export default function getVisibleTodos(todos, filter) {
|
||||
switch (filter) {
|
||||
case VisibilityFilters.SHOW_ALL:
|
||||
return todos;
|
||||
case VisibilityFilters.SHOW_ACTIVE:
|
||||
return todos.filter(todo => !todo.completed);
|
||||
case VisibilityFilters.SHOW_COMPLETED:
|
||||
return todos.filter(todo => todo.completed);
|
||||
default:
|
||||
return todos;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import { combineReducers } from "redux";
|
||||
import { combineReducers } from 'redux';
|
||||
|
||||
import lists from "./lists";
|
||||
import visibilityFilter from "./visibilityFilter";
|
||||
import lists from './lists';
|
||||
import visibilityFilter from './visibilityFilter';
|
||||
|
||||
const todoApp = combineReducers({
|
||||
lists,
|
||||
visibilityFilter
|
||||
visibilityFilter,
|
||||
});
|
||||
|
||||
export default todoApp;
|
||||
|
||||
@@ -1,62 +1,58 @@
|
||||
import {
|
||||
ADD_ITEM,
|
||||
REMOVE_ITEM,
|
||||
TOGGLE_ITEM,
|
||||
ADD_TODO,
|
||||
REMOVE_TODO,
|
||||
TOGGLE_TODO,
|
||||
RECIEVE_TODOS,
|
||||
REQUEST_TODOS,
|
||||
INVALIDATE_TODOS,
|
||||
VALIDATE_TODOS
|
||||
} from "../actions";
|
||||
VALIDATE_TODOS,
|
||||
EDIT_TODO,
|
||||
} from '../actions';
|
||||
|
||||
export default function items(
|
||||
state = { dirty: true, fetching: false, todos: [] },
|
||||
action
|
||||
) {
|
||||
export default function todos(state = { dirty: true, fetching: false, todos: [] }, action) {
|
||||
switch (action.type) {
|
||||
case RECIEVE_TODOS:
|
||||
return {
|
||||
...state,
|
||||
dirty: false,
|
||||
fetching: false,
|
||||
todos: action.todos
|
||||
todos: action.todos,
|
||||
};
|
||||
case ADD_ITEM:
|
||||
case ADD_TODO:
|
||||
return {
|
||||
...state,
|
||||
todos: [...state.todos, action.todo]
|
||||
todos: [...state.todos, action.todo],
|
||||
};
|
||||
case INVALIDATE_TODOS:
|
||||
return {
|
||||
...state,
|
||||
dirty: true
|
||||
dirty: true,
|
||||
};
|
||||
case VALIDATE_TODOS:
|
||||
return {
|
||||
...state,
|
||||
dirty: false
|
||||
dirty: false,
|
||||
};
|
||||
case EDIT_TODO:
|
||||
return {
|
||||
...state,
|
||||
todos: state.todos.map(todo => (todo.id === action.id ? action.todo : todo)),
|
||||
};
|
||||
case REQUEST_TODOS:
|
||||
return {
|
||||
...state,
|
||||
fetching: true
|
||||
fetching: true,
|
||||
};
|
||||
case REMOVE_ITEM:
|
||||
case REMOVE_TODO:
|
||||
return {
|
||||
...state,
|
||||
todos: state.todos.filter(item => item.id !== action.id)
|
||||
todos: state.todos.filter(todo => todo.id !== action.id),
|
||||
};
|
||||
case TOGGLE_TODO: {
|
||||
return {
|
||||
...state,
|
||||
todos: state.todos.map(todo => (todo.id === action.id ? { ...todo, completed: !todo.completed } : todo)),
|
||||
};
|
||||
case TOGGLE_ITEM: {
|
||||
const itemsArray = [...state.todos];
|
||||
itemsArray.some((item, i) => {
|
||||
if (item.id === action.id) {
|
||||
const newItem = { ...item };
|
||||
newItem.completed = !item.completed;
|
||||
itemsArray[i] = newItem;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return { ...state, todos: itemsArray };
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
|
||||
@@ -4,22 +4,25 @@ import {
|
||||
VALIDATE_LISTS,
|
||||
REQUEST_LISTS,
|
||||
RECIEVE_TODOS,
|
||||
ADD_ITEM,
|
||||
ADD_TODO,
|
||||
INVALIDATE_TODOS,
|
||||
VALIDATE_TODOS,
|
||||
REQUEST_TODOS,
|
||||
REMOVE_ITEM,
|
||||
TOGGLE_ITEM,
|
||||
REMOVE_TODO,
|
||||
TOGGLE_TODO,
|
||||
RECIEVE_LISTS,
|
||||
ADD_LIST,
|
||||
REMOVE_LIST
|
||||
} from "../actions";
|
||||
REMOVE_LIST,
|
||||
EDIT_LIST,
|
||||
} from '../actions';
|
||||
|
||||
import list from "./list";
|
||||
import list from './list';
|
||||
|
||||
export default function lists(
|
||||
state = { dirty: true, fetching: false, lists: {} },
|
||||
action
|
||||
state = {
|
||||
dirty: true, fetching: false, lists: {}, list: '',
|
||||
},
|
||||
action,
|
||||
) {
|
||||
switch (action.type) {
|
||||
case CHANGE_LIST:
|
||||
@@ -29,48 +32,55 @@ export default function lists(
|
||||
...state,
|
||||
dirty: false,
|
||||
fetching: false,
|
||||
lists: action.lists
|
||||
lists: action.lists,
|
||||
};
|
||||
case ADD_LIST:
|
||||
return {
|
||||
...state,
|
||||
lists: { ...state.lists, [action.list.id]: action.list }
|
||||
lists: { ...state.lists, [action.list.id]: action.list },
|
||||
};
|
||||
case REMOVE_LIST:
|
||||
case REMOVE_LIST: {
|
||||
const newLists = { ...state.lists };
|
||||
delete newLists[action.list];
|
||||
delete newLists[action.id];
|
||||
return {
|
||||
...state,
|
||||
lists: newLists
|
||||
lists: newLists,
|
||||
};
|
||||
}
|
||||
case EDIT_LIST: {
|
||||
return {
|
||||
...state,
|
||||
lists: { ...state.lists, [action.id]: action.list },
|
||||
};
|
||||
}
|
||||
case INVALIDATE_LISTS:
|
||||
return {
|
||||
...state,
|
||||
dirty: true
|
||||
dirty: true,
|
||||
};
|
||||
case VALIDATE_LISTS:
|
||||
return {
|
||||
...state,
|
||||
dirty: false
|
||||
dirty: false,
|
||||
};
|
||||
case REQUEST_LISTS:
|
||||
return {
|
||||
...state,
|
||||
fetching: true
|
||||
fetching: true,
|
||||
};
|
||||
case RECIEVE_TODOS:
|
||||
case ADD_ITEM:
|
||||
case ADD_TODO:
|
||||
case INVALIDATE_TODOS:
|
||||
case VALIDATE_TODOS:
|
||||
case REQUEST_TODOS:
|
||||
case REMOVE_ITEM:
|
||||
case TOGGLE_ITEM:
|
||||
case REMOVE_TODO:
|
||||
case TOGGLE_TODO:
|
||||
return {
|
||||
...state,
|
||||
lists: {
|
||||
...state.lists,
|
||||
[action.list]: list(state.lists[action.list], action)
|
||||
}
|
||||
[state.list]: list(state.lists[state.list], action),
|
||||
},
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { VisibilityFilters, SET_VISIBILITY_FILTER } from "../actions";
|
||||
import { VisibilityFilters, SET_VISIBILITY_FILTER } from '../actions';
|
||||
|
||||
const { SHOW_ALL } = VisibilityFilters;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user