mirror of
https://github.com/usatiuk/ustk-todolist.git
synced 2025-10-28 15:47:48 +01:00
offline remove, toggle, edit todos
This commit is contained in:
28
package-lock.json
generated
28
package-lock.json
generated
@@ -3084,12 +3084,14 @@
|
|||||||
"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"
|
||||||
@@ -3104,17 +3106,20 @@
|
|||||||
"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",
|
||||||
@@ -3231,7 +3236,8 @@
|
|||||||
"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",
|
||||||
@@ -3243,6 +3249,7 @@
|
|||||||
"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"
|
||||||
}
|
}
|
||||||
@@ -3257,6 +3264,7 @@
|
|||||||
"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"
|
||||||
}
|
}
|
||||||
@@ -3264,12 +3272,14 @@
|
|||||||
"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"
|
||||||
@@ -3288,6 +3298,7 @@
|
|||||||
"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"
|
||||||
}
|
}
|
||||||
@@ -3368,7 +3379,8 @@
|
|||||||
"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",
|
||||||
@@ -3380,6 +3392,7 @@
|
|||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
@@ -3501,6 +3514,7 @@
|
|||||||
"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",
|
||||||
|
|||||||
28
react/package-lock.json
generated
28
react/package-lock.json
generated
@@ -4143,11 +4143,13 @@
|
|||||||
},
|
},
|
||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"brace-expansion": {
|
"brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"bundled": true,
|
"bundled": 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"
|
||||||
@@ -4160,15 +4162,18 @@
|
|||||||
},
|
},
|
||||||
"code-point-at": {
|
"code-point-at": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"concat-map": {
|
"concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"console-control-strings": {
|
"console-control-strings": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"core-util-is": {
|
"core-util-is": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
@@ -4271,7 +4276,8 @@
|
|||||||
},
|
},
|
||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"ini": {
|
"ini": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
@@ -4281,6 +4287,7 @@
|
|||||||
"is-fullwidth-code-point": {
|
"is-fullwidth-code-point": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"number-is-nan": "^1.0.0"
|
"number-is-nan": "^1.0.0"
|
||||||
}
|
}
|
||||||
@@ -4293,17 +4300,20 @@
|
|||||||
"minimatch": {
|
"minimatch": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.8",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"minipass": {
|
"minipass": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.4",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"safe-buffer": "^5.1.1",
|
"safe-buffer": "^5.1.1",
|
||||||
"yallist": "^3.0.0"
|
"yallist": "^3.0.0"
|
||||||
@@ -4320,6 +4330,7 @@
|
|||||||
"mkdirp": {
|
"mkdirp": {
|
||||||
"version": "0.5.1",
|
"version": "0.5.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"minimist": "0.0.8"
|
"minimist": "0.0.8"
|
||||||
}
|
}
|
||||||
@@ -4392,7 +4403,8 @@
|
|||||||
},
|
},
|
||||||
"number-is-nan": {
|
"number-is-nan": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"object-assign": {
|
"object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
@@ -4402,6 +4414,7 @@
|
|||||||
"once": {
|
"once": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
@@ -4507,6 +4520,7 @@
|
|||||||
"string-width": {
|
"string-width": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"bundled": true,
|
"bundled": 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",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { API_ROOT, getToken } from './util';
|
import { API_ROOT, getToken } from './util';
|
||||||
import { fetchLists } from './lists';
|
import { fetchLists, INVALIDATE_LISTS } from './lists';
|
||||||
|
|
||||||
export const ADD_TODO = 'ADD_TODO';
|
export const ADD_TODO = 'ADD_TODO';
|
||||||
export const REMOVE_TODO = 'REMOVE_TODO';
|
export const REMOVE_TODO = 'REMOVE_TODO';
|
||||||
@@ -70,66 +70,78 @@ export function addTodo(text) {
|
|||||||
|
|
||||||
export function removeTodo(id) {
|
export function removeTodo(id) {
|
||||||
return async dispatch => {
|
return async dispatch => {
|
||||||
dispatch(invalidateTodos());
|
dispatch({
|
||||||
const response = await fetch(`${API_ROOT}/todos/${id}`, {
|
type: REMOVE_TODO,
|
||||||
|
id,
|
||||||
|
meta: {
|
||||||
|
offline: {
|
||||||
|
effect: {
|
||||||
|
url: `${API_ROOT}/todos/${id}`,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${getToken()}`,
|
Authorization: `Bearer ${getToken()}`,
|
||||||
'content-type': 'application/json',
|
'content-type': 'application/json',
|
||||||
},
|
},
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
|
},
|
||||||
|
rollback: {
|
||||||
|
type: INVALIDATE_LISTS,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const json = await response.json();
|
|
||||||
if (json.success) {
|
|
||||||
dispatch({ type: REMOVE_TODO, id });
|
|
||||||
} else {
|
|
||||||
dispatch(fetchLists());
|
|
||||||
}
|
|
||||||
dispatch(validateTodos());
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toggleTodo(id) {
|
export function toggleTodo(id) {
|
||||||
return async (dispatch, getState) => {
|
return async (dispatch, getState) => {
|
||||||
dispatch(invalidateTodos());
|
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const todoObj = state.todos.todos[id];
|
const todoObj = state.todos.todos[id];
|
||||||
const completed = !todoObj.completed;
|
const completed = !todoObj.completed;
|
||||||
const response = await fetch(`${API_ROOT}/todos/${id}`, {
|
dispatch({
|
||||||
|
type: TOGGLE_TODO,
|
||||||
|
id,
|
||||||
|
meta: {
|
||||||
|
offline: {
|
||||||
|
effect: {
|
||||||
|
url: `${API_ROOT}/todos/${id}`,
|
||||||
body: JSON.stringify({ completed }),
|
body: JSON.stringify({ completed }),
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${getToken()}`,
|
Authorization: `Bearer ${getToken()}`,
|
||||||
'content-type': 'application/json',
|
'content-type': 'appl ication/json',
|
||||||
},
|
},
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
|
},
|
||||||
|
rollback: {
|
||||||
|
type: INVALIDATE_LISTS,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const json = await response.json();
|
|
||||||
if (json.success) {
|
|
||||||
dispatch({ type: TOGGLE_TODO, id });
|
|
||||||
} else {
|
|
||||||
dispatch(fetchLists());
|
|
||||||
}
|
|
||||||
dispatch(validateTodos());
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function editTodo(id, text) {
|
export function editTodo(id, text) {
|
||||||
return async dispatch => {
|
return async dispatch => {
|
||||||
dispatch(invalidateTodos());
|
dispatch({
|
||||||
const response = await fetch(`${API_ROOT}/todos/${id}`, {
|
type: EDIT_TODO,
|
||||||
|
id,
|
||||||
|
text,
|
||||||
|
meta: {
|
||||||
|
offline: {
|
||||||
|
effect: {
|
||||||
|
url: `${API_ROOT}/todos/${id}`,
|
||||||
body: JSON.stringify({ text }),
|
body: JSON.stringify({ text }),
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${getToken()}`,
|
Authorization: `Bearer ${getToken()}`,
|
||||||
'content-type': 'application/json',
|
'content-type': 'application/json',
|
||||||
},
|
},
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
|
},
|
||||||
|
rollback: {
|
||||||
|
type: INVALIDATE_LISTS,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const json = await response.json();
|
|
||||||
if (json.success) {
|
|
||||||
const todo = json.data;
|
|
||||||
dispatch({ type: EDIT_TODO, id, todo });
|
|
||||||
} else {
|
|
||||||
dispatch(fetchLists());
|
|
||||||
}
|
|
||||||
dispatch(validateTodos());
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,21 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import LogoutLink from '../containers/LogoutLink';
|
import LogoutLink from '../containers/LogoutLink';
|
||||||
import FetchButton from '../containers/FetchButton';
|
import FetchButton from '../containers/FetchButton';
|
||||||
import Status from '../containers/Status';
|
import Status from '../containers/Status';
|
||||||
|
|
||||||
export default function UserHeader() {
|
export default class UserHeader extends React.Component {
|
||||||
|
componentDidUpdate() {
|
||||||
|
if (
|
||||||
|
(this.props.dirtyLists || this.props.dirtyTodos) &&
|
||||||
|
!this.props.fetchingLists
|
||||||
|
) {
|
||||||
|
this.props.fetchLists();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
return (
|
return (
|
||||||
<div id="user-header">
|
<div id="user-header">
|
||||||
<FetchButton>sync</FetchButton>
|
<FetchButton>sync</FetchButton>
|
||||||
@@ -12,4 +23,12 @@ export default function UserHeader() {
|
|||||||
<LogoutLink>logout</LogoutLink>
|
<LogoutLink>logout</LogoutLink>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UserHeader.propTypes = {
|
||||||
|
dirtyLists: PropTypes.bool.isRequired,
|
||||||
|
dirtyTodos: PropTypes.bool.isRequired,
|
||||||
|
fetchingLists: PropTypes.bool.isRequired,
|
||||||
|
fetchLists: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ function Status({ userFetching, listsFetching }) {
|
|||||||
marginLeft: '1rem',
|
marginLeft: '1rem',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{userFetching ? 'user' : null}
|
{userFetching ? 'loading user' : null}
|
||||||
{listsFetching ? 'lists' : null}
|
{listsFetching ? 'loading lists' : null}
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,23 @@
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import UserHeader from '../components/UserHeader';
|
import UserHeader from '../components/UserHeader';
|
||||||
|
import { fetchLists } from '../actions/lists';
|
||||||
|
|
||||||
function mapStateToProps(state) {
|
function mapStateToProps(state) {
|
||||||
return {
|
return {
|
||||||
user: state.user,
|
user: state.user,
|
||||||
|
dirtyLists: state.lists.dirty,
|
||||||
|
dirtyTodos: state.todos.dirty,
|
||||||
|
fetchingLists: state.lists.fetching,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(mapStateToProps)(UserHeader);
|
function mapDispatchToProps(dispatch) {
|
||||||
|
return {
|
||||||
|
fetchLists: () => dispatch(fetchLists()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps,
|
||||||
|
)(UserHeader);
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export default function todos(
|
|||||||
...state,
|
...state,
|
||||||
todos: {
|
todos: {
|
||||||
...state.todos,
|
...state.todos,
|
||||||
[action.id]: action.todo,
|
[action.id]: { ...state.todos[action.id], text: action.text },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
case REQUEST_TODOS:
|
case REQUEST_TODOS:
|
||||||
|
|||||||
Reference in New Issue
Block a user