diff --git a/src/actions/index.ts b/src/actions/index.ts index 6b0ce2c..e2f11ae 100644 --- a/src/actions/index.ts +++ b/src/actions/index.ts @@ -1,28 +1,63 @@ -import * as redux from 'redux' - import { api } from '../api' + import * as state from '../reducers/index' type Q = { request: T } type S = { response: T } type E = { error: Error } -type QEmpty = Q +type QEmpty = Q<{}> type QValue = Q<{ value: number }> +export interface Dispatch { + (a: A): A +} + +type _T = Action['type'] + +type APIActionGroup = + ({ type: TQ } & Q<_Q>) +| ({ type: TS } & Q<_Q> & S<_S>) +| ({ type: TE } & Q<_Q> & E) + +type Thunk = (request: Q) => Promise + +const createThunkAction = + (fn: Thunk, tq: TQ, ts: TS, te: TE) => + (request: Q) => + (dispatch: Dispatch>) => { + dispatch({ type: tq, request }) + fn(request) + .then(response => dispatch({ type: ts, request, response })) + .catch(error => dispatch({ type: te, request, error })) + } + +type LoadAction = + ({ type: 'LOAD_COUNT_REQUEST' } & QEmpty) +| ({ type: 'LOAD_COUNT_SUCCESS' } & QEmpty & S<{ value: number }>) +| ({ type: 'LOAD_COUNT_ERROR' } & QEmpty & E) + +type SaveAction = + ({ type: 'SAVE_COUNT_REQUEST' } & QValue) +| ({ type: 'SAVE_COUNT_SUCCESS' } & QValue & S<{}>) +| ({ type: 'SAVE_COUNT_ERROR' } & QValue & E) + export type Action = + LoadAction +| SaveAction // UI actions - { type: 'INCREMENT_COUNTER', delta: number } +| { type: 'INCREMENT_COUNTER', delta: number } | { type: 'RESET_COUNTER' } -// API Requests -| ({ type: 'SAVE_COUNT_REQUEST' } & QValue) -| ({ type: 'SAVE_COUNT_SUCCESS' } & QValue & S<{}>) -| ({ type: 'SAVE_COUNT_ERROR' } & QValue & E) +export const saveCount = createThunkAction(api.save, + 'SAVE_COUNT_REQUEST', + 'SAVE_COUNT_SUCCESS', + 'SAVE_COUNT_ERROR') -| ({ type: 'LOAD_COUNT_REQUEST' } & QEmpty) -| ({ type: 'LOAD_COUNT_SUCCESS' } & QEmpty & S<{ value: number }>) -| ({ type: 'LOAD_COUNT_ERROR' } & QEmpty & E) +export const loadCount = createThunkAction(api.load, + 'LOAD_COUNT_REQUEST', + 'LOAD_COUNT_SUCCESS', + 'LOAD_COUNT_ERROR') export const incrementCounter = (delta: number): Action => ({ type: 'INCREMENT_COUNTER', @@ -32,41 +67,3 @@ export const incrementCounter = (delta: number): Action => ({ export const resetCounter = (): Action => ({ type: 'RESET_COUNTER', }) - -export type ApiActionGroup<_Q, _S> = { - request: (q?: _Q) => Action & Q<_Q> - success: (s: _S, q?: _Q) => Action & Q<_Q> & S<_S> - error: (e: Error, q?: _Q) => Action & Q<_Q> & E -} - -const _saveCount: ApiActionGroup<{ value: number }, {}> = { - request: (request) => - ({ type: 'SAVE_COUNT_REQUEST', request }), - success: (response, request) => - ({ type: 'SAVE_COUNT_SUCCESS', request, response }), - error: (error, request) => - ({ type: 'SAVE_COUNT_ERROR', request, error }), -} - -const _loadCount: ApiActionGroup = { - request: (request) => - ({ type: 'LOAD_COUNT_REQUEST', request: null }), - success: (response, request) => - ({ type: 'LOAD_COUNT_SUCCESS', request: null, response }), - error: (error, request) => - ({ type: 'LOAD_COUNT_ERROR', request: null, error }), -} - -type apiFunc = (q: Q) => Promise - -function apiActionGroupFactory(x: ApiActionGroup, go: apiFunc) { - return (request: Q) => (dispatch: redux.Dispatch) => { - dispatch(x.request(request)) - go(request) - .then((response) => dispatch(x.success(response, request))) - .catch((e: Error) => dispatch(x.error(e, request))) - } -} - -export const saveCount = apiActionGroupFactory(_saveCount, api.save) -export const loadCount = () => apiActionGroupFactory(_loadCount, api.load)(null) diff --git a/src/api.ts b/src/api.ts index 4521b28..6f6870e 100644 --- a/src/api.ts +++ b/src/api.ts @@ -29,13 +29,13 @@ export type Api = { export const api: Api = { save: (counter: Counter): Promise => flakify(() => { - localStorage.setItem('__counterValue', counter.value.toString()) - return null - }), + localStorage.setItem('__counterValue', counter.value.toString()) + return null + }), load: (): Promise => flakify(() => { - const storedValue = parseInt(localStorage.getItem('__counterValue'), 10) - return { - value: storedValue || 0, - } - }), + const storedValue = parseInt(localStorage.getItem('__counterValue'), 10) + return { + value: storedValue || 0, + } + }), } diff --git a/src/components/counter.tsx b/src/components/counter.tsx index c132e32..2c6765b 100644 --- a/src/components/counter.tsx +++ b/src/components/counter.tsx @@ -43,7 +43,7 @@ const mapDispatchToProps = (dispatch: redux.Dispatch): ConnectedDispa increment: (n: number) => dispatch(incrementCounter(n)), load: () => - dispatch(loadCount()), + dispatch(loadCount({})), save: (value: number) => dispatch(saveCount({ value })), })