Playing with sagas for a stuf implementation of tasks
This commit is contained in:
@@ -1,13 +1,16 @@
|
||||
import { AppState } from "@kredens/frontend/store";
|
||||
import { deleteTask, scheduleTask } from "@kredens/frontend/store/tasks/actions";
|
||||
import { Task, TaskScheduleType } from "@kredens/frontend/store/tasks/types";
|
||||
import React, { useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
export default () => {
|
||||
const [taskName, setTaskName] = useState("");
|
||||
const tasks = useSelector<AppState, { [key: string]: Task }>(state => state.tasks.items);
|
||||
const dispatch = useDispatch();
|
||||
useEffect(() => {
|
||||
dispatch({type: "FETCH_TASKS"});
|
||||
}, [])
|
||||
|
||||
const onTaskAddClick = () => {
|
||||
dispatch(scheduleTask(Math.random().toString(36), {
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { combineReducers, createStore } from "redux";
|
||||
import { applyMiddleware, combineReducers, createStore } from "redux";
|
||||
import { composeWithDevTools } from "redux-devtools-extension";
|
||||
import createSagaMiddleware from "redux-saga";
|
||||
import rootSaga from "./sagas";
|
||||
import { tasksReducer } from "./tasks/reducers";
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
@@ -7,8 +10,14 @@ const rootReducer = combineReducers({
|
||||
|
||||
export type AppState = ReturnType<typeof rootReducer>;
|
||||
|
||||
const sagaMiddleware = createSagaMiddleware();
|
||||
export default function configureStore() {
|
||||
const store = createStore(rootReducer);
|
||||
const store = createStore(
|
||||
rootReducer,
|
||||
composeWithDevTools(applyMiddleware(sagaMiddleware))
|
||||
);
|
||||
|
||||
sagaMiddleware.run(rootSaga);
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
35
src/frontend/store/sagas.ts
Normal file
35
src/frontend/store/sagas.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { all, call, put, takeEvery } from "redux-saga/effects";
|
||||
import { getTasks, Task as APITask } from "../api/tasks";
|
||||
import { taskFetchError, taskFetchOk, taskFetchStart } from "./tasks/actions";
|
||||
import { Task, TaskQuery, TaskScheduleType } from "./tasks/types";
|
||||
|
||||
export function* fetchTasksSaga(query: TaskQuery = { limit: 10 }) {
|
||||
yield put(taskFetchStart(query));
|
||||
|
||||
try {
|
||||
const tasks: APITask[] = yield call(getTasks);
|
||||
yield put(
|
||||
taskFetchOk(
|
||||
query,
|
||||
tasks
|
||||
.map<[string, Task]>(t => [
|
||||
t.id.toString(),
|
||||
{
|
||||
name: t.name,
|
||||
schedule: {
|
||||
type: TaskScheduleType.Once,
|
||||
due: t.due.toISO()
|
||||
}
|
||||
}
|
||||
])
|
||||
.reduce((res, [id, task]) => ({ ...res, [id]: task }), {})
|
||||
)
|
||||
);
|
||||
} catch (error) {
|
||||
yield put(taskFetchError(query, `${error}`));
|
||||
}
|
||||
}
|
||||
|
||||
export default function* rootSaga() {
|
||||
yield all([yield takeEvery("FETCH_TASKS", fetchTasksSaga)]);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Task, TasksAction, TasksActionType } from "./types";
|
||||
import { DateTime } from "luxon";
|
||||
import { Task, TaskQuery, TasksAction, TasksActionType } from "./types";
|
||||
|
||||
export function scheduleTask(id: string, task: Task): TasksActionType {
|
||||
return {
|
||||
@@ -14,3 +15,35 @@ export function deleteTask(id: string): TasksActionType {
|
||||
id
|
||||
};
|
||||
}
|
||||
|
||||
export function taskFetchStart(query: TaskQuery): TasksActionType {
|
||||
return {
|
||||
type: TasksAction.TASKS_FETCH_START,
|
||||
query,
|
||||
started: DateTime.utc().toISO()
|
||||
};
|
||||
}
|
||||
|
||||
export function taskFetchOk(
|
||||
query: TaskQuery,
|
||||
results: { [key: string]: Task }
|
||||
): TasksActionType {
|
||||
return {
|
||||
type: TasksAction.TASKS_FETCH_OK,
|
||||
query,
|
||||
results,
|
||||
fetched: DateTime.utc().toISO()
|
||||
};
|
||||
}
|
||||
|
||||
export function taskFetchError(
|
||||
query: TaskQuery,
|
||||
error: string
|
||||
): TasksActionType {
|
||||
return {
|
||||
type: TasksAction.TASKS_FETCH_ERROR,
|
||||
query,
|
||||
error,
|
||||
fetched: DateTime.utc().toISO()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import objectHash from "object-hash";
|
||||
import { TasksAction, TasksActionType, TasksState } from "./types";
|
||||
|
||||
const initialState: TasksState = {
|
||||
items: {}
|
||||
items: {},
|
||||
queries: {}
|
||||
};
|
||||
|
||||
export function tasksReducer(
|
||||
@@ -24,6 +26,45 @@ export function tasksReducer(
|
||||
.filter(([key]) => key !== action.id)
|
||||
.reduce((res, [key, task]) => ({ ...res, [key]: task }), {})
|
||||
};
|
||||
case TasksAction.TASKS_FETCH_START:
|
||||
return {
|
||||
...state,
|
||||
queries: {
|
||||
...state.queries,
|
||||
[objectHash(action.query)]: {
|
||||
result: "fetching",
|
||||
started: action.started
|
||||
}
|
||||
}
|
||||
};
|
||||
case TasksAction.TASKS_FETCH_OK:
|
||||
return {
|
||||
...state,
|
||||
items: {
|
||||
...state.items,
|
||||
...action.results
|
||||
},
|
||||
queries: {
|
||||
...state.queries,
|
||||
[objectHash(action.query)]: {
|
||||
result: "ok",
|
||||
items: Object.keys(action.results),
|
||||
fetched: action.fetched
|
||||
}
|
||||
}
|
||||
};
|
||||
case TasksAction.TASKS_FETCH_ERROR:
|
||||
return {
|
||||
...state,
|
||||
queries: {
|
||||
...state.queries,
|
||||
[objectHash(action.query)]: {
|
||||
result: "error",
|
||||
error: action.error,
|
||||
fetched: action.fetched
|
||||
}
|
||||
}
|
||||
};
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -19,26 +19,82 @@ export interface Task {
|
||||
schedule: TaskSchedule;
|
||||
}
|
||||
|
||||
export interface TaskQuery {
|
||||
query?: string;
|
||||
after?: string;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
interface TaskQueryOk {
|
||||
result: "ok";
|
||||
items: string[];
|
||||
fetched: string;
|
||||
}
|
||||
|
||||
interface TaskQueryFetching {
|
||||
result: "fetching";
|
||||
started: string;
|
||||
}
|
||||
|
||||
interface TaskQueryError {
|
||||
result: "error";
|
||||
error: string;
|
||||
fetched: string;
|
||||
}
|
||||
|
||||
export type TaskQueryResult = TaskQueryOk | TaskQueryFetching | TaskQueryError;
|
||||
|
||||
export interface TasksState {
|
||||
items: {
|
||||
[key: string]: Task;
|
||||
};
|
||||
queries: {
|
||||
[key: string]: TaskQueryResult;
|
||||
};
|
||||
}
|
||||
|
||||
export enum TasksAction {
|
||||
SCHEDULE_TASK = "SCHEDULE_TASK",
|
||||
DELETE_TASK = "DELETE_TASK"
|
||||
DELETE_TASK = "DELETE_TASK",
|
||||
TASKS_FETCH_START = "TASKS_FETCH_START",
|
||||
TASKS_FETCH_OK = "TASKS_FETCH_OK",
|
||||
TASKS_FETCH_ERROR = "TASKS_FETCH_ERROR"
|
||||
}
|
||||
|
||||
interface ScheduleTaskAction {
|
||||
type: typeof TasksAction.SCHEDULE_TASK;
|
||||
type: TasksAction.SCHEDULE_TASK;
|
||||
id: string;
|
||||
task: Task;
|
||||
}
|
||||
|
||||
interface DeleteTaskAction {
|
||||
type: typeof TasksAction.DELETE_TASK;
|
||||
type: TasksAction.DELETE_TASK;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export type TasksActionType = ScheduleTaskAction | DeleteTaskAction;
|
||||
interface TaskFetchStartAction {
|
||||
type: TasksAction.TASKS_FETCH_START;
|
||||
query: TaskQuery;
|
||||
started: string;
|
||||
}
|
||||
|
||||
interface TaskFetchOkAction {
|
||||
type: TasksAction.TASKS_FETCH_OK;
|
||||
query: TaskQuery;
|
||||
results: { [key: string]: Task };
|
||||
fetched: string;
|
||||
}
|
||||
|
||||
interface TaskFetchErrorAction {
|
||||
type: TasksAction.TASKS_FETCH_ERROR;
|
||||
query: TaskQuery;
|
||||
error: string;
|
||||
fetched: string;
|
||||
}
|
||||
|
||||
export type TasksActionType =
|
||||
| ScheduleTaskAction
|
||||
| DeleteTaskAction
|
||||
| TaskFetchOkAction
|
||||
| TaskFetchStartAction
|
||||
| TaskFetchErrorAction;
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
{
|
||||
"extends": "../../tslint.json",
|
||||
"rules": {
|
||||
"no-implicit-dependencies": [true, "dev", ["@kredens/frontend"]]
|
||||
}
|
||||
}
|
||||
"extends": "../../tslint.json",
|
||||
"rules": {
|
||||
"no-implicit-dependencies": [
|
||||
true,
|
||||
"dev",
|
||||
["@kredens/frontend"]
|
||||
],
|
||||
"no-submodule-imports": [
|
||||
true,
|
||||
"@kredens/frontend",
|
||||
"redux-saga/effects"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user