mirror of
https://github.com/usatiuk/ustk-todolist.git
synced 2025-10-28 23:57:49 +01:00
add offline support
This commit is contained in:
5
react/.vscode/settings.json
vendored
5
react/.vscode/settings.json
vendored
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"editor.tabSize": 2,
|
||||
"editor.insertSpaces": true,
|
||||
"prettier.eslintIntegration": true
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { API_ROOT, getToken } from './util';
|
||||
import { API_ROOT, getToken, mongoObjectId } from './util';
|
||||
import { RECIEVE_TODOS } from './todos';
|
||||
|
||||
export const ADD_LIST = 'ADD_LIST';
|
||||
@@ -20,17 +20,9 @@ function requestLists() {
|
||||
function recieveLists(lists) {
|
||||
return { type: RECIEVE_LISTS, lists };
|
||||
}
|
||||
|
||||
function invalidateLists() {
|
||||
return { type: INVALIDATE_LISTS };
|
||||
}
|
||||
function validateLists() {
|
||||
return { type: VALIDATE_LISTS };
|
||||
}
|
||||
export function changeList(list) {
|
||||
return { type: CHANGE_LIST, list };
|
||||
}
|
||||
|
||||
export function startCreateList() {
|
||||
return { type: START_CREATE_LIST };
|
||||
}
|
||||
@@ -46,44 +38,57 @@ export function stopEditList() {
|
||||
|
||||
export function addList(name) {
|
||||
return async dispatch => {
|
||||
dispatch(invalidateLists());
|
||||
const response = await fetch(`${API_ROOT}/lists`, {
|
||||
body: JSON.stringify({ name }),
|
||||
headers: {
|
||||
Authorization: `Bearer ${getToken()}`,
|
||||
'content-type': 'application/json',
|
||||
const id = mongoObjectId();
|
||||
dispatch({
|
||||
type: ADD_LIST,
|
||||
list: {
|
||||
name,
|
||||
id,
|
||||
todos: [],
|
||||
},
|
||||
meta: {
|
||||
offline: {
|
||||
effect: {
|
||||
url: `${API_ROOT}/lists`,
|
||||
body: JSON.stringify({ name, id }),
|
||||
headers: {
|
||||
Authorization: `Bearer ${getToken()}`,
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
method: 'POST',
|
||||
},
|
||||
rollback: {
|
||||
type: INVALIDATE_LISTS,
|
||||
},
|
||||
},
|
||||
},
|
||||
method: 'POST',
|
||||
});
|
||||
const json = await response.json();
|
||||
const list = json.data;
|
||||
dispatch({ type: ADD_LIST, list });
|
||||
dispatch(changeList(list.id));
|
||||
dispatch(validateLists());
|
||||
};
|
||||
}
|
||||
|
||||
export function removeList() {
|
||||
return async (dispatch, getState) => {
|
||||
let state = getState();
|
||||
const state = getState();
|
||||
const { list } = state.lists;
|
||||
dispatch(invalidateLists());
|
||||
const response = await fetch(`${API_ROOT}/lists/${list}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${getToken()}`,
|
||||
'content-type': 'application/json',
|
||||
dispatch({
|
||||
type: REMOVE_LIST,
|
||||
list,
|
||||
meta: {
|
||||
offline: {
|
||||
effect: {
|
||||
url: `${API_ROOT}/lists/${list}`,
|
||||
headers: {
|
||||
Authorization: `Bearer ${getToken()}`,
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
method: 'DELETE',
|
||||
},
|
||||
rollback: {
|
||||
type: INVALIDATE_LISTS,
|
||||
},
|
||||
},
|
||||
},
|
||||
method: 'DELETE',
|
||||
});
|
||||
const json = await response.json();
|
||||
if (json.success) {
|
||||
dispatch({ type: REMOVE_LIST, list });
|
||||
state = getState();
|
||||
const lists = Object.values(state.lists.lists);
|
||||
const newList = lists.length ? lists[lists.length - 1].id : '';
|
||||
dispatch(changeList(newList));
|
||||
}
|
||||
dispatch(validateLists());
|
||||
};
|
||||
}
|
||||
|
||||
@@ -91,20 +96,27 @@ export function editList(name) {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const { list } = state.lists;
|
||||
dispatch(invalidateLists());
|
||||
const response = await fetch(`${API_ROOT}/lists/${list}`, {
|
||||
body: JSON.stringify({ name }),
|
||||
headers: {
|
||||
Authorization: `Bearer ${getToken()}`,
|
||||
'content-type': 'application/json',
|
||||
dispatch({
|
||||
type: EDIT_LIST_NAME,
|
||||
list,
|
||||
name,
|
||||
meta: {
|
||||
offline: {
|
||||
effect: {
|
||||
url: `${API_ROOT}/lists/${list}`,
|
||||
body: JSON.stringify({ name }),
|
||||
headers: {
|
||||
Authorization: `Bearer ${getToken()}`,
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
method: 'PATCH',
|
||||
},
|
||||
rollback: {
|
||||
type: INVALIDATE_LISTS,
|
||||
},
|
||||
},
|
||||
},
|
||||
method: 'PATCH',
|
||||
});
|
||||
const json = await response.json();
|
||||
if (json.success) {
|
||||
dispatch({ type: EDIT_LIST_NAME, list, name });
|
||||
}
|
||||
dispatch(validateLists());
|
||||
};
|
||||
}
|
||||
|
||||
@@ -131,29 +143,19 @@ export function fetchLists() {
|
||||
});
|
||||
const json = await response.json();
|
||||
const lists = json.data;
|
||||
const listsObj = lists.reduce((obj, list) => {
|
||||
const listsObj = lists.reduce((obj, curList) => {
|
||||
const newObj = { ...obj };
|
||||
newObj[list.id] = {
|
||||
newObj[curList.id] = {
|
||||
dirty: true,
|
||||
fetching: false,
|
||||
editing: false,
|
||||
...list,
|
||||
todos: list.todos.map(todo => todo.id),
|
||||
...curList,
|
||||
todos: curList.todos.map(todo => todo.id),
|
||||
};
|
||||
return newObj;
|
||||
}, {});
|
||||
|
||||
dispatch({ type: RECIEVE_TODOS, todos: normalizeTodos(lists) });
|
||||
dispatch(recieveLists(listsObj));
|
||||
if (lists.length !== 0) {
|
||||
dispatch(changeList(listsObj[Object.keys(listsObj)[0]].id));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function loadLists() {
|
||||
return async dispatch => {
|
||||
dispatch(requestLists());
|
||||
dispatch(fetchLists());
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { API_ROOT, getToken } from './util';
|
||||
import { fetchLists, INVALIDATE_LISTS } from './lists';
|
||||
import { API_ROOT, getToken, mongoObjectId } from './util';
|
||||
import { INVALIDATE_LISTS } from './lists';
|
||||
|
||||
export const ADD_TODO = 'ADD_TODO';
|
||||
export const REMOVE_TODO = 'REMOVE_TODO';
|
||||
@@ -21,13 +21,6 @@ export function setVisibilityFilter(filter) {
|
||||
return { type: SET_VISIBILITY_FILTER, filter };
|
||||
}
|
||||
|
||||
function invalidateTodos() {
|
||||
return { type: INVALIDATE_TODOS };
|
||||
}
|
||||
function validateTodos() {
|
||||
return { type: VALIDATE_TODOS };
|
||||
}
|
||||
|
||||
export function fetchTodos() {
|
||||
return async dispatch => {
|
||||
dispatch({ type: REQUEST_TODOS });
|
||||
@@ -46,24 +39,32 @@ export function addTodo(text) {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const { list } = state.lists;
|
||||
const id = mongoObjectId();
|
||||
if (list) {
|
||||
dispatch(invalidateTodos());
|
||||
const response = await fetch(`${API_ROOT}/lists/${list}/todos`, {
|
||||
body: JSON.stringify({ text }),
|
||||
headers: {
|
||||
Authorization: `Bearer ${getToken()}`,
|
||||
'content-type': 'application/json',
|
||||
dispatch({
|
||||
type: ADD_TODO,
|
||||
todo: {
|
||||
text,
|
||||
id,
|
||||
completed: false,
|
||||
},
|
||||
meta: {
|
||||
offline: {
|
||||
effect: {
|
||||
url: `${API_ROOT}/lists/${list}/todos`,
|
||||
body: JSON.stringify({ text, id }),
|
||||
headers: {
|
||||
Authorization: `Bearer ${getToken()}`,
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
method: 'POST',
|
||||
},
|
||||
rollback: {
|
||||
type: INVALIDATE_LISTS,
|
||||
},
|
||||
},
|
||||
},
|
||||
method: 'POST',
|
||||
});
|
||||
const json = await response.json();
|
||||
const todo = json.data;
|
||||
if (json.success) {
|
||||
dispatch({ type: ADD_TODO, todo });
|
||||
} else {
|
||||
dispatch(fetchLists());
|
||||
}
|
||||
dispatch(validateTodos());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { API_ROOT, getToken, setToken } from './util';
|
||||
import { loadLists } from './lists';
|
||||
import { fetchLists } from './lists';
|
||||
|
||||
export const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
|
||||
export const LOGIN_FAIL = 'LOGIN_FAIL';
|
||||
@@ -40,7 +40,7 @@ export function loadUser() {
|
||||
const json = await response.json();
|
||||
if (json.success) {
|
||||
dispatch(loginSuccess(json.data));
|
||||
dispatch(loadLists());
|
||||
dispatch(fetchLists());
|
||||
} else {
|
||||
dispatch(loginFail(json.error));
|
||||
}
|
||||
@@ -64,7 +64,7 @@ export function login(user) {
|
||||
if (json.success) {
|
||||
setToken(json.data.jwt);
|
||||
dispatch(loginSuccess(json.data));
|
||||
dispatch(loadLists());
|
||||
dispatch(fetchLists());
|
||||
} else {
|
||||
dispatch(loginFail(json.error));
|
||||
}
|
||||
@@ -93,7 +93,7 @@ export function signup(user) {
|
||||
if (json.success) {
|
||||
setToken(json.data.jwt);
|
||||
dispatch(signupSuccess(json.data));
|
||||
dispatch(loadLists());
|
||||
dispatch(fetchLists());
|
||||
} else {
|
||||
dispatch(signupFail(json.error));
|
||||
}
|
||||
|
||||
@@ -9,3 +9,15 @@ export function setToken(_token) {
|
||||
export function getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
export function mongoObjectId() {
|
||||
// eslint-disable-next-line
|
||||
const timestamp = ((new Date().getTime() / 1000) | 0).toString(16);
|
||||
return (
|
||||
timestamp +
|
||||
'xxxxxxxxxxxxxxxx'
|
||||
// eslint-disable-next-line
|
||||
.replace(/[x]/g, () => ((Math.random() * 16) | 0).toString(16))
|
||||
.toLowerCase()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -40,14 +40,23 @@ export default function lists(
|
||||
};
|
||||
case CHANGE_LIST:
|
||||
return { ...state, list: action.list };
|
||||
case RECIEVE_LISTS:
|
||||
case RECIEVE_LISTS: {
|
||||
const newLists = Object.values(action.lists);
|
||||
let { list } = state;
|
||||
if (newLists.length !== 0) {
|
||||
if (!newLists.some(curList => curList.id === list)) {
|
||||
list = newLists[0].id;
|
||||
}
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
dirty: false,
|
||||
loaded: true,
|
||||
fetching: false,
|
||||
lists: action.lists,
|
||||
list,
|
||||
};
|
||||
}
|
||||
case START_CREATE_LIST:
|
||||
return {
|
||||
...state,
|
||||
@@ -63,13 +72,16 @@ export default function lists(
|
||||
...state,
|
||||
creating: false,
|
||||
lists: { ...state.lists, [action.list.id]: action.list },
|
||||
list: action.list.id,
|
||||
};
|
||||
case REMOVE_LIST: {
|
||||
const newLists = { ...state.lists };
|
||||
delete newLists[action.list];
|
||||
const listsObjs = Object.values(newLists);
|
||||
const list = listsObjs.length ? listsObjs[listsObjs.length - 1].id : '';
|
||||
return {
|
||||
...state,
|
||||
list: null,
|
||||
list,
|
||||
lists: newLists,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user