This commit is contained in:
Sean Yesmunt 2017-12-03 22:27:55 -05:00
parent e7a7754a4f
commit c000ad1bc8
14 changed files with 381 additions and 109 deletions

View file

@ -12,6 +12,9 @@ flow-typed
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue suppress_comment=\\(.\\|\n\\)*\\$FlowIssue
module.name_mapper='^constants\(.*\)$' -> '<PROJECT_ROOT>/js/constants\1' module.name_mapper='^constants\(.*\)$' -> '<PROJECT_ROOT>/js/constants\1'
module.name_mapper='^util\(.*\)$' -> '<PROJECT_ROOT>/js/util\1'
module.name_mapper='^redux\(.*\)$' -> '<PROJECT_ROOT>/js/redux\1' module.name_mapper='^redux\(.*\)$' -> '<PROJECT_ROOT>/js/redux\1'
module.name_mapper='^types\(.*\)$' -> '<PROJECT_ROOT>/js/types\1'
module.name_mapper='^component\(.*\)$' -> '<PROJECT_ROOT>/js/component\1'
[strict] [strict]

3
src/renderer/flow-typed/bluebird.js vendored Normal file
View file

@ -0,0 +1,3 @@
declare module 'bluebird' {
declare module.exports: any;
}

3
src/renderer/flow-typed/classnames.js vendored Normal file
View file

@ -0,0 +1,3 @@
declare module 'classnames' {
declare module.exports: any;
}

3
src/renderer/flow-typed/formik.js vendored Normal file
View file

@ -0,0 +1,3 @@
declare module 'formik' {
declare module.exports: any;
}

1
src/renderer/flow-typed/i18n.js vendored Normal file
View file

@ -0,0 +1 @@
declare function __(a: string): string;

View file

@ -0,0 +1,3 @@
declare module 'qrcode.react' {
declare module.exports: any;
}

View file

@ -0,0 +1,3 @@
declare module 'shapeshift.io' {
declare module.exports: any;
}

View file

@ -1,12 +1,27 @@
import React, { PureComponent } from "react"; // @flow
import * as React from "react";
import QRCode from "qrcode.react"; import QRCode from "qrcode.react";
import * as statuses from "constants/shape_shift"; import * as statuses from "constants/shape_shift";
import Address from "component/address"; import Address from "component/address";
import Link from "component/link"; import Link from "component/link";
import type { Dispatch } from "redux/actions/shape_shift";
export default class ActiveShapeShift extends PureComponent { type Props = {
shiftState: ?string,
shiftCoinType: ?string,
shiftDepositAddress: ?string,
shiftReturnAddress: ?string,
shiftOrderId: ?string,
originCoinDepositMax: ?number,
clearShapeShift: Dispatch,
doShowSnackBar: Dispatch,
getActiveShift: Dispatch,
};
class ActiveShapeShift extends React.PureComponent<Props> {
constructor() { constructor() {
super(); super();
// $FlowFixMe
this.continousFetch = undefined; this.continousFetch = undefined;
} }
@ -14,12 +29,13 @@ export default class ActiveShapeShift extends PureComponent {
const { getActiveShift, shiftDepositAddress } = this.props; const { getActiveShift, shiftDepositAddress } = this.props;
getActiveShift(shiftDepositAddress); getActiveShift(shiftDepositAddress);
// $FlowFixMe
this.continousFetch = setInterval(() => { this.continousFetch = setInterval(() => {
getActiveShift(shiftDepositAddress); getActiveShift(shiftDepositAddress);
}, 10000); }, 10000);
} }
componentWillUpdate(nextProps) { componentWillUpdate(nextProps: Props) {
const { shiftState } = nextProps; const { shiftState } = nextProps;
if (shiftState === statuses.COMPLETE) { if (shiftState === statuses.COMPLETE) {
this.clearContinuousFetch(); this.clearContinuousFetch();
@ -31,7 +47,9 @@ export default class ActiveShapeShift extends PureComponent {
} }
clearContinuousFetch() { clearContinuousFetch() {
/// $FlowFixMe
clearInterval(this.continousFetch); clearInterval(this.continousFetch);
// $FlowFixMe
this.continousFetch = null; this.continousFetch = null;
} }
@ -42,7 +60,7 @@ export default class ActiveShapeShift extends PureComponent {
shiftReturnAddress, shiftReturnAddress,
shiftOrderId, shiftOrderId,
shiftState, shiftState,
shiftDepositLimit, originCoinDepositMax,
clearShapeShift, clearShapeShift,
doShowSnackBar, doShowSnackBar,
} = this.props; } = this.props;
@ -54,7 +72,7 @@ export default class ActiveShapeShift extends PureComponent {
<p> <p>
Send up to{" "} Send up to{" "}
<span className="credit-amount--bold"> <span className="credit-amount--bold">
{shiftDepositLimit}{" "} {originCoinDepositMax}{" "}
<span className="credit-amount--colored">{shiftCoinType}</span> <span className="credit-amount--colored">{shiftCoinType}</span>
</span>{" "} </span>{" "}
to the address below. to the address below.
@ -102,13 +120,15 @@ export default class ActiveShapeShift extends PureComponent {
: __("Cancel") : __("Cancel")
} }
/> />
<span className="shapeshift__link"> {shiftOrderId && (
<Link <span className="shapeshift__link">
button="text" <Link
label={__("View the status on Shapeshift.io")} button="text"
href={`https://shapeshift.io/#/status/${shiftOrderId}`} label={__("View the status on Shapeshift.io")}
/> href={`https://shapeshift.io/#/status/${shiftOrderId}`}
</span> />
</span>
)}
{shiftState === statuses.NO_DEPOSITS && {shiftState === statuses.NO_DEPOSITS &&
shiftReturnAddress && ( shiftReturnAddress && (
<div className="shapeshift__actions-help"> <div className="shapeshift__actions-help">
@ -123,3 +143,5 @@ export default class ActiveShapeShift extends PureComponent {
); );
} }
} }
export default ActiveShapeShift;

View file

@ -2,25 +2,48 @@ import React from "react";
import Link from "component/link"; import Link from "component/link";
import { getExampleAddress } from "util/shape_shift"; import { getExampleAddress } from "util/shape_shift";
import { Submit, FormRow } from "component/form"; import { Submit, FormRow } from "component/form";
import type { ShapeShiftFormValues, Dispatch } from "redux/actions/shape_shift";
export default ({ type ShapeShiftFormErrors = {
values, returnAddress?: string,
errors, };
touched,
handleChange, type Props = {
handleBlur, values: ShapeShiftFormValues,
handleSubmit, errors: ShapeShiftFormErrors,
resetForm, touched: boolean,
isSubmitting, handleChange: Event => any,
shiftSupportedCoins, handleBlur: Event => any,
originCoin, handleSubmit: Event => any,
updating, isSubmitting: boolean,
getCoinStats, shiftSupportedCoins: Array<string>,
receiveAddress, originCoin: string,
originCoinDepositMax, updating: boolean,
originCoinDepositMin, getCoinStats: Dispatch,
originCoinDepositFee, receiveAddress: string,
}) => { originCoinDepositFee: number,
originCoinDepositMin: string,
originCoinDepositMax: number,
};
export default (props: Props) => {
const {
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
isSubmitting,
shiftSupportedCoins,
originCoin,
updating,
getCoinStats,
receiveAddress,
originCoinDepositMax,
originCoinDepositMin,
originCoinDepositFee,
} = props;
return ( return (
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<div className="form-field"> <div className="form-field">

View file

@ -1,4 +1,5 @@
import React from "react"; // @flow
import * as React from "react";
import { shell } from "electron"; import { shell } from "electron";
import { Formik } from "formik"; import { Formik } from "formik";
import classnames from "classnames"; import classnames from "classnames";
@ -8,9 +9,23 @@ import Link from "component/link";
import Spinner from "component/common/spinner"; import Spinner from "component/common/spinner";
import { BusyMessage } from "component/common"; import { BusyMessage } from "component/common";
import ShapeShiftForm from "./internal/form"; import ShapeShiftForm from "./internal/form";
import ActiveShift from "./internal/active-shift"; import ActiveShapeShift from "./internal/active-shift";
class ShapeShift extends React.PureComponent { import type { ShapeShiftState } from "redux/reducers/shape_shift";
import type { Dispatch, ShapeShiftFormValues } from "redux/actions/shape_shift";
type Props = {
shapeShift: ShapeShiftState,
getCoinStats: Dispatch,
createShapeShift: Dispatch,
clearShapeShift: Dispatch,
getActiveShift: Dispatch,
doShowSnackBar: Dispatch,
shapeShiftInit: Dispatch,
receiveAddress: string,
};
class ShapeShift extends React.PureComponent<Props> {
componentDidMount() { componentDidMount() {
const { const {
shapeShiftInit, shapeShiftInit,
@ -49,11 +64,15 @@ class ShapeShift extends React.PureComponent {
shiftReturnAddress, shiftReturnAddress,
shiftCoinType, shiftCoinType,
shiftOrderId, shiftOrderId,
cancelShapeShift,
shiftState, shiftState,
origin,
} = shapeShift; } = shapeShift;
const initialFormValues: ShapeShiftFormValues = {
receiveAddress,
originCoin: "BTC",
returnAddress: "",
};
return ( return (
// add the "shapeshift__intital-wrapper class so we can avoid content jumping once everything loads" // add the "shapeshift__intital-wrapper class so we can avoid content jumping once everything loads"
// it just gives the section a min-height equal to the height of the content when the form is rendered // it just gives the section a min-height equal to the height of the content when the form is rendered
@ -85,11 +104,7 @@ class ShapeShift extends React.PureComponent {
<Formik <Formik
onSubmit={createShapeShift} onSubmit={createShapeShift}
validate={validateShapeShiftForm} validate={validateShapeShiftForm}
initialValues={{ initialValues={initialFormValues}
receiveAddress,
originCoin: "BTC",
returnAddress: "",
}}
render={formProps => ( render={formProps => (
<ShapeShiftForm <ShapeShiftForm
{...formProps} {...formProps}
@ -106,12 +121,12 @@ class ShapeShift extends React.PureComponent {
/> />
)} )}
{hasActiveShift && ( {hasActiveShift && (
<ActiveShift <ActiveShapeShift
getActiveShift={getActiveShift} getActiveShift={getActiveShift}
shiftCoinType={shiftCoinType} shiftCoinType={shiftCoinType}
shiftReturnAddress={shiftReturnAddress} shiftReturnAddress={shiftReturnAddress}
shiftDepositAddress={shiftDepositAddress} shiftDepositAddress={shiftDepositAddress}
shiftDepositLimit={originCoinDepositMax} originCoinDepositMax={originCoinDepositMax}
shiftOrderId={shiftOrderId} shiftOrderId={shiftOrderId}
shiftState={shiftState} shiftState={shiftState}
clearShapeShift={clearShapeShift} clearShapeShift={clearShapeShift}

View file

@ -1,11 +1,56 @@
import { createAction } from "util/redux-utils"; // @flow
import Promise from "bluebird"; import Promise from "bluebird";
import * as types from "constants/action_types"; import * as types from "constants/action_types";
import { coinRegexPatterns } from "util/shape_shift"; import { coinRegexPatterns } from "util/shape_shift";
import type {
GetSupportedCoinsSuccess,
GetCoinStatsStart,
GetCoinStatsSuccess,
GetCoinStatsFail,
PrepareShapeShiftSuccess,
PrepareShapeShiftFail,
GetActiveShiftSuccess,
GetActiveShiftFail,
} from "redux/reducers/shape_shift";
import type { FormikActions } from "types/common";
// use promise chains instead of callbacks for shapeshift api
const shapeShift = Promise.promisifyAll(require("shapeshift.io")); const shapeShift = Promise.promisifyAll(require("shapeshift.io"));
export const shapeShiftInit = () => dispatch => { // All ShapeShift actions
// Action types defined in the reducer will contain some payload
export type Action =
| { type: types.GET_SUPPORTED_COINS_START }
| { type: types.GET_SUPPORTED_COINS_FAIL }
| GetSupportedCoinsSuccess
| GetCoinStatsStart
| { type: types.GET_COIN_STATS_START }
| GetCoinStatsFail
| GetCoinStatsSuccess
| { type: types.PREPARE_SHAPE_SHIFT_START }
| PrepareShapeShiftFail
| PrepareShapeShiftSuccess
| { type: types.GET_ACTIVE_SHIFT_START }
| GetActiveShiftFail
| GetActiveShiftSuccess;
// Basic thunk types
// It would be nice to import these from types/common
// Not sure how that would work since they rely on the Action type
type PromiseAction = Promise<Action>;
type ThunkAction = (dispatch: Dispatch) => any;
export type Dispatch = (
action: Action | ThunkAction | PromiseAction | Array<Action>
) => any;
// ShapeShift form values
export type ShapeShiftFormValues = {
originCoin: string,
returnAddress: ?string,
receiveAddress: string,
};
export const shapeShiftInit = () => (dispatch: Dispatch): ThunkAction => {
dispatch({ type: types.GET_SUPPORTED_COINS_START }); dispatch({ type: types.GET_SUPPORTED_COINS_START });
return shapeShift return shapeShift
@ -34,8 +79,9 @@ export const shapeShiftInit = () => dispatch => {
); );
}; };
export const getCoinStats = coin => dispatch => { export const getCoinStats = (coin: string) => (
// TODO: get ShapeShift fee dispatch: Dispatch
): ThunkAction => {
const pair = `${coin.toLowerCase()}_lbc`; const pair = `${coin.toLowerCase()}_lbc`;
dispatch({ type: types.GET_COIN_STATS_START, data: coin }); dispatch({ type: types.GET_COIN_STATS_START, data: coin });
@ -48,7 +94,10 @@ export const getCoinStats = coin => dispatch => {
.catch(err => dispatch({ type: types.GET_COIN_STATS_FAIL, data: err })); .catch(err => dispatch({ type: types.GET_COIN_STATS_FAIL, data: err }));
}; };
export const createShapeShift = (values, actions) => dispatch => { export const createShapeShift = (
values: ShapeShiftFormValues,
actions: FormikActions
) => (dispatch: Dispatch): ThunkAction => {
const { const {
originCoin, originCoin,
returnAddress, returnAddress,
@ -68,12 +117,14 @@ export const createShapeShift = (values, actions) => dispatch => {
) )
.catch(err => { .catch(err => {
dispatch({ type: types.PREPARE_SHAPE_SHIFT_FAIL, data: err }); dispatch({ type: types.PREPARE_SHAPE_SHIFT_FAIL, data: err });
// for formik // for formik to stop the submit
actions.setSubmitting(false); actions.setSubmitting(false);
}); });
}; };
export const getActiveShift = depositAddress => dispatch => { export const getActiveShift = (depositAddress: string) => (
dispatch: Dispatch
): ThunkAction => {
dispatch({ type: types.GET_ACTIVE_SHIFT_START }); dispatch({ type: types.GET_ACTIVE_SHIFT_START });
return shapeShift return shapeShift
@ -82,5 +133,5 @@ export const getActiveShift = depositAddress => dispatch => {
.catch(err => dispatch({ type: types.GET_ACTIVE_SHIFT_FAIL, data: err })); .catch(err => dispatch({ type: types.GET_ACTIVE_SHIFT_FAIL, data: err }));
}; };
export const clearShapeShift = () => dispatch => export const clearShapeShift = () => (dispatch: Dispatch): Action =>
dispatch({ type: types.CLEAR_SHAPE_SHIFT }); dispatch({ type: types.CLEAR_SHAPE_SHIFT });

View file

@ -1,14 +1,88 @@
// @flow
import { handleActions } from "util/redux-utils"; import { handleActions } from "util/redux-utils";
import * as types from "constants/action_types"; import * as actions from "constants/action_types";
import * as statuses from "constants/shape_shift"; import * as statuses from "constants/shape_shift";
const defaultState = { export type ShapeShiftState = {
loading: boolean,
updating: boolean,
shiftSupportedCoins: Array<string>,
hasActiveShift: boolean,
originCoin: ?string,
error: ?string,
shiftDepositAddress: ?string,
shiftReturnAddress: ?string,
shiftCoinType: ?string,
shiftOrderId: ?string,
shiftState: ?string,
originCoinDepositMax: ?number,
// originCoinDepositMin is a string because we need to convert it from scientifc notation
// it will usually be something like 0.00000001 coins
// using Number(x) or parseInt(x) will either change it back to scientific notation or round to zero
originCoinDepositMin: ?string,
originCoinDepositFee: ?number,
};
// All ShapeShift actions that will have some payload
export type GetSupportedCoinsSuccess = {
type: actions.GET_SUPPORTED_COINS_SUCCESS,
data: Array<string>,
};
export type GetCoinStatsStart = {
type: actions.GET_SUPPORTED_COINS_SUCCESS,
data: string,
};
export type GetCoinStatsSuccess = {
type: actions.GET_COIN_STATS_SUCCESS,
data: ShapeShiftMarketInfo,
};
export type GetCoinStatsFail = {
type: actions.GET_COIN_STATS_FAIL,
data: string,
};
export type PrepareShapeShiftSuccess = {
type: actions.PREPARE_SHAPE_SHIFT_SUCCESS,
data: ActiveShiftInfo,
};
export type PrepareShapeShiftFail = {
type: actions.PREPARE_SHAPE_SHIFT_FAIL,
data: ShapeShiftErrorResponse,
};
export type GetActiveShiftSuccess = {
type: actions.GET_ACTIVE_SHIFT_SUCCESS,
data: string,
};
export type GetActiveShiftFail = {
type: actions.GET_ACTIVE_SHIFT_FAIL,
data: ShapeShiftErrorResponse,
};
// ShapeShift sub-types
// Defined for actions that contain an object in the payload
type ShapeShiftMarketInfo = {
limit: number,
minimum: number,
minerFee: number,
};
type ActiveShiftInfo = {
deposit: string,
depositType: string,
returnAddress: string,
orderId: string,
};
type ShapeShiftErrorResponse = {
message: string,
};
const defaultState: ShapeShiftState = {
loading: true, loading: true,
updating: false, updating: false,
error: undefined,
shiftSupportedCoins: [], shiftSupportedCoins: [],
originCoin: undefined,
hasActiveShift: false, hasActiveShift: false,
originCoin: undefined,
error: undefined,
shiftDepositAddress: undefined, // shapeshift address to send your coins to shiftDepositAddress: undefined, // shapeshift address to send your coins to
shiftReturnAddress: undefined, shiftReturnAddress: undefined,
shiftCoinType: undefined, shiftCoinType: undefined,
@ -21,61 +95,108 @@ const defaultState = {
export default handleActions( export default handleActions(
{ {
[types.GET_SUPPORTED_COINS_START]: () => ({ [actions.GET_SUPPORTED_COINS_START]: (
state: ShapeShiftState
): ShapeShiftState => ({
...state,
loading: true, loading: true,
error: undefined, error: undefined,
}), }),
[types.GET_SUPPORTED_COINS_SUCCESS]: ( [actions.GET_SUPPORTED_COINS_SUCCESS]: (
state, state: ShapeShiftState,
{ data: shiftSupportedCoins } action: GetSupportedCoinsSuccess
) => ({ ): ShapeShiftState => {
error: undefined, const shiftSupportedCoins = action.data;
shiftSupportedCoins, return {
}), ...state,
[types.GET_SUPPORTED_COINS_FAIL]: () => ({ error: undefined,
shiftSupportedCoins,
};
},
[actions.GET_SUPPORTED_COINS_FAIL]: (
state: ShapeShiftState
): ShapeShiftState => ({
...state,
loading: false, loading: false,
error: true, error: "Error getting available coins",
}), }),
[types.GET_COIN_STATS_START]: (state, { data: coin }) => ({ [actions.GET_COIN_STATS_START]: (
updating: true, state: ShapeShiftState,
originCoin: coin, action: GetCoinStatsStart
}), ): ShapeShiftState => {
[types.GET_COIN_STATS_SUCCESS]: (state, { data: marketInfo }) => ({ const coin = action.data;
loading: false, return {
updating: false, ...state,
originCoinDepositMax: marketInfo.limit, updating: true,
// this will come in scientific notation originCoin: coin,
// toFixed shows the real number, then regex to remove trailing zeros };
originCoinDepositMin: marketInfo.minimum },
.toFixed(10) [actions.GET_COIN_STATS_SUCCESS]: (
.replace(/\.?0+$/, ""), state: ShapeShiftState,
originCoinDepositFee: marketInfo.minerFee, action: GetCoinStatsSuccess
}), ): ShapeShiftState => {
[types.GET_COIN_STATS_FAIL]: (state, { data: error }) => ({ const marketInfo: ShapeShiftMarketInfo = action.data;
loading: false,
error,
}),
[types.PREPARE_SHAPE_SHIFT_START]: () => ({ return {
...state,
loading: false,
updating: false,
originCoinDepositMax: marketInfo.limit,
// this will come in scientific notation
// toFixed shows the real number, then regex to remove trailing zeros
originCoinDepositMin: marketInfo.minimum
.toFixed(10)
.replace(/\.?0+$/, ""),
originCoinDepositFee: marketInfo.minerFee,
};
},
[actions.GET_COIN_STATS_FAIL]: (
state: ShapeShiftState,
action: GetCoinStatsFail
): ShapeShiftState => {
const error = action.data;
return {
...state,
loading: false,
error,
};
},
[actions.PREPARE_SHAPE_SHIFT_START]: (
state: ShapeShiftState
): ShapeShiftState => ({
...state,
error: undefined, error: undefined,
}), }),
[types.PREPARE_SHAPE_SHIFT_SUCCESS]: ( [actions.PREPARE_SHAPE_SHIFT_SUCCESS]: (
state, state: ShapeShiftState,
{ data: { deposit, depositType, returnAddress, orderId } } action: PrepareShapeShiftSuccess
) => ({ ) => {
hasActiveShift: true, const activeShiftInfo: ActiveShiftInfo = action.data;
shiftDepositAddress: deposit, return {
shiftCoinType: depositType, ...state,
shiftReturnAddress: returnAddress, hasActiveShift: true,
shiftOrderId: orderId, shiftDepositAddress: activeShiftInfo.deposit,
shiftState: statuses.NO_DEPOSITS, shiftCoinType: activeShiftInfo.depositType,
}), shiftReturnAddress: activeShiftInfo.returnAddress,
[types.PREPARE_SHAPE_SHIFT_FAIL]: (state, { data: error }) => ({ shiftOrderId: activeShiftInfo.orderId,
error: error.message, shiftState: statuses.NO_DEPOSITS,
}), };
},
[actions.PREPARE_SHAPE_SHIFT_FAIL]: (
state: ShapeShiftState,
action: PrepareShapeShiftFail
) => {
const error: ShapeShiftErrorResponse = action.data;
return {
...state,
error: error.message,
};
},
[types.CLEAR_SHAPE_SHIFT]: () => ({ [actions.CLEAR_SHAPE_SHIFT]: (state: ShapeShiftState): ShapeShiftState => ({
...state,
loading: false, loading: false,
updating: false, updating: false,
hasActiveShift: false, hasActiveShift: false,
@ -83,21 +204,38 @@ export default handleActions(
shiftReturnAddress: undefined, shiftReturnAddress: undefined,
shiftCoinType: undefined, shiftCoinType: undefined,
shiftOrderId: undefined, shiftOrderId: undefined,
originCoin: "BTC", originCoin: state.shiftSupportedCoins[0],
}), }),
[types.GET_ACTIVE_SHIFT_START]: () => ({ [actions.GET_ACTIVE_SHIFT_START]: (
state: ShapeShiftState
): ShapeShiftState => ({
...state,
error: undefined, error: undefined,
updating: true, updating: true,
}), }),
[types.GET_ACTIVE_SHIFT_SUCCESS]: (state, { data: status }) => ({ [actions.GET_ACTIVE_SHIFT_SUCCESS]: (
updating: false, state: ShapeShiftState,
shiftState: status, action: GetActiveShiftSuccess
}), ): ShapeShiftState => {
[types.GET_ACTIVE_SHIFT_FAIL]: (state, { data: error }) => ({ const status = action.data;
updating: false, return {
error: error.message, ...state,
}), updating: false,
shiftState: status,
};
},
[actions.GET_ACTIVE_SHIFT_FAIL]: (
state: ShapeShiftState,
action: GetActiveShiftFail
): ShapeShiftState => {
const error: ShapeShiftErrorResponse = action.data;
return {
...state,
updating: false,
error: error.message,
};
},
}, },
defaultState defaultState
); );

View file

@ -0,0 +1,3 @@
export type FormikActions = {
setSubmitting: boolean => mixed,
};

View file

@ -1,6 +1,7 @@
// util for creating reducers // util for creating reducers
// based off of redux-actions // based off of redux-actions
// https://redux-actions.js.org/docs/api/handleAction.html#handleactions // https://redux-actions.js.org/docs/api/handleAction.html#handleactions
export const handleActions = (actionMap, defaultState) => { export const handleActions = (actionMap, defaultState) => {
return (state = defaultState, action) => { return (state = defaultState, action) => {
const handler = actionMap[action.type]; const handler = actionMap[action.type];