aboutsummaryrefslogtreecommitdiffstats
path: root/packages/instant/src/redux/reducer.ts
diff options
context:
space:
mode:
authorFabio Berger <me@fabioberger.com>2018-11-19 20:49:16 +0800
committerFabio Berger <me@fabioberger.com>2018-11-19 20:49:16 +0800
commit8175192f60e2c2827f6e7d664fbe8bf2d9ddee9c (patch)
tree587ce5e026674e5665ad164995aac69462290aed /packages/instant/src/redux/reducer.ts
parent7d2c975d7335155b85a7549c25b953d0afacf5cf (diff)
parent94de441de744ed53470335122a38e265c3a71aff (diff)
downloaddexon-0x-contracts-8175192f60e2c2827f6e7d664fbe8bf2d9ddee9c.tar
dexon-0x-contracts-8175192f60e2c2827f6e7d664fbe8bf2d9ddee9c.tar.gz
dexon-0x-contracts-8175192f60e2c2827f6e7d664fbe8bf2d9ddee9c.tar.bz2
dexon-0x-contracts-8175192f60e2c2827f6e7d664fbe8bf2d9ddee9c.tar.lz
dexon-0x-contracts-8175192f60e2c2827f6e7d664fbe8bf2d9ddee9c.tar.xz
dexon-0x-contracts-8175192f60e2c2827f6e7d664fbe8bf2d9ddee9c.tar.zst
dexon-0x-contracts-8175192f60e2c2827f6e7d664fbe8bf2d9ddee9c.zip
Merge branch 'development'
* development: (957 commits) fix(order_utils.py): publish docs to S3, not RTD (#1264) fix: make instant package private feat: refer to map file in postpublish configs feat: add new bundle name to bundle watch fix: tslint ignore rule in wrong place Update blog post feature Fix disclaimer on mobile Add smart contract docs to Developer Home Add Apache license link Fix capitalization in title Remove excess semi-colon Point directly to README for docs link Update icons Update LICENSE Fix disclaimer Add blogpost URL Add disclaimer Add launch kit to Developer home list of tools feat: Deploy contracts to Rinkeby fix: fix exceeds block gas limit error ...
Diffstat (limited to 'packages/instant/src/redux/reducer.ts')
-rw-r--r--packages/instant/src/redux/reducer.ts288
1 files changed, 288 insertions, 0 deletions
diff --git a/packages/instant/src/redux/reducer.ts b/packages/instant/src/redux/reducer.ts
new file mode 100644
index 000000000..dfc2b89f3
--- /dev/null
+++ b/packages/instant/src/redux/reducer.ts
@@ -0,0 +1,288 @@
+import { BuyQuote } from '@0x/asset-buyer';
+import { AssetProxyId, ObjectMap } from '@0x/types';
+import { BigNumber } from '@0x/utils';
+import { Web3Wrapper } from '@0x/web3-wrapper';
+import * as _ from 'lodash';
+
+import { LOADING_ACCOUNT, LOCKED_ACCOUNT } from '../constants';
+import { assetMetaDataMap } from '../data/asset_meta_data_map';
+import {
+ Account,
+ AccountReady,
+ AccountState,
+ AffiliateInfo,
+ Asset,
+ AssetMetaData,
+ AsyncProcessState,
+ DisplayStatus,
+ Network,
+ OrderProcessState,
+ OrderState,
+ ProviderState,
+ StandardSlidingPanelContent,
+ StandardSlidingPanelSettings,
+} from '../types';
+
+import { Action, ActionTypes } from './actions';
+
+// State that is required and we have defaults for, before props are passed in
+export interface DefaultState {
+ network: Network;
+ assetMetaDataMap: ObjectMap<AssetMetaData>;
+ buyOrderState: OrderState;
+ latestErrorDisplayStatus: DisplayStatus;
+ quoteRequestState: AsyncProcessState;
+ standardSlidingPanelSettings: StandardSlidingPanelSettings;
+}
+
+// State that is required but needs to be derived from the props
+interface PropsDerivedState {
+ providerState: ProviderState;
+}
+
+// State that is optional
+interface OptionalState {
+ selectedAsset: Asset;
+ availableAssets: Asset[];
+ selectedAssetUnitAmount: BigNumber;
+ ethUsdPrice: BigNumber;
+ latestBuyQuote: BuyQuote;
+ latestErrorMessage: string;
+ affiliateInfo: AffiliateInfo;
+}
+
+export type State = DefaultState & PropsDerivedState & Partial<OptionalState>;
+
+export const DEFAULT_STATE: DefaultState = {
+ network: Network.Mainnet,
+ assetMetaDataMap,
+ buyOrderState: { processState: OrderProcessState.None },
+ latestErrorDisplayStatus: DisplayStatus.Hidden,
+ quoteRequestState: AsyncProcessState.None,
+ standardSlidingPanelSettings: {
+ animationState: 'none',
+ content: StandardSlidingPanelContent.None,
+ },
+};
+
+export const createReducer = (initialState: State) => {
+ const reducer = (state: State = initialState, action: Action): State => {
+ switch (action.type) {
+ case ActionTypes.SET_ACCOUNT_STATE_LOADING:
+ return reduceStateWithAccount(state, LOADING_ACCOUNT);
+ case ActionTypes.SET_ACCOUNT_STATE_LOCKED:
+ return reduceStateWithAccount(state, LOCKED_ACCOUNT);
+ case ActionTypes.SET_ACCOUNT_STATE_READY: {
+ const address = action.data;
+ let newAccount: AccountReady = {
+ state: AccountState.Ready,
+ address,
+ };
+ const currentAccount = state.providerState.account;
+ if (currentAccount.state === AccountState.Ready && currentAccount.address === address) {
+ newAccount = {
+ ...newAccount,
+ ethBalanceInWei: currentAccount.ethBalanceInWei,
+ };
+ }
+ return reduceStateWithAccount(state, newAccount);
+ }
+ case ActionTypes.UPDATE_ACCOUNT_ETH_BALANCE: {
+ const { address, ethBalanceInWei } = action.data;
+ const currentAccount = state.providerState.account;
+ if (currentAccount.state !== AccountState.Ready || currentAccount.address !== address) {
+ return state;
+ } else {
+ const newAccount: AccountReady = {
+ ...currentAccount,
+ ethBalanceInWei,
+ };
+ return reduceStateWithAccount(state, newAccount);
+ }
+ }
+ case ActionTypes.UPDATE_ETH_USD_PRICE:
+ return {
+ ...state,
+ ethUsdPrice: action.data,
+ };
+ case ActionTypes.UPDATE_SELECTED_ASSET_UNIT_AMOUNT:
+ return {
+ ...state,
+ selectedAssetUnitAmount: action.data,
+ };
+ case ActionTypes.UPDATE_LATEST_BUY_QUOTE:
+ const newBuyQuoteIfExists = action.data;
+ const shouldUpdate =
+ _.isUndefined(newBuyQuoteIfExists) || doesBuyQuoteMatchState(newBuyQuoteIfExists, state);
+ if (shouldUpdate) {
+ return {
+ ...state,
+ latestBuyQuote: newBuyQuoteIfExists,
+ quoteRequestState: AsyncProcessState.Success,
+ };
+ } else {
+ return state;
+ }
+ case ActionTypes.SET_QUOTE_REQUEST_STATE_PENDING:
+ return {
+ ...state,
+ latestBuyQuote: undefined,
+ quoteRequestState: AsyncProcessState.Pending,
+ };
+ case ActionTypes.SET_QUOTE_REQUEST_STATE_FAILURE:
+ return {
+ ...state,
+ latestBuyQuote: undefined,
+ quoteRequestState: AsyncProcessState.Failure,
+ };
+ case ActionTypes.SET_BUY_ORDER_STATE_NONE:
+ return {
+ ...state,
+ buyOrderState: { processState: OrderProcessState.None },
+ };
+ case ActionTypes.SET_BUY_ORDER_STATE_VALIDATING:
+ return {
+ ...state,
+ buyOrderState: { processState: OrderProcessState.Validating },
+ };
+ case ActionTypes.SET_BUY_ORDER_STATE_PROCESSING:
+ const processingData = action.data;
+ const { startTimeUnix, expectedEndTimeUnix } = processingData;
+ return {
+ ...state,
+ buyOrderState: {
+ processState: OrderProcessState.Processing,
+ txHash: processingData.txHash,
+ progress: {
+ startTimeUnix,
+ expectedEndTimeUnix,
+ },
+ },
+ };
+ case ActionTypes.SET_BUY_ORDER_STATE_FAILURE:
+ const failureTxHash = action.data;
+ if ('txHash' in state.buyOrderState) {
+ if (state.buyOrderState.txHash === failureTxHash) {
+ const { txHash, progress } = state.buyOrderState;
+ return {
+ ...state,
+ buyOrderState: {
+ processState: OrderProcessState.Failure,
+ txHash,
+ progress,
+ },
+ };
+ }
+ }
+ return state;
+ case ActionTypes.SET_BUY_ORDER_STATE_SUCCESS:
+ const successTxHash = action.data;
+ if ('txHash' in state.buyOrderState) {
+ if (state.buyOrderState.txHash === successTxHash) {
+ const { txHash, progress } = state.buyOrderState;
+ return {
+ ...state,
+ buyOrderState: {
+ processState: OrderProcessState.Success,
+ txHash,
+ progress,
+ },
+ };
+ }
+ }
+ return state;
+ case ActionTypes.SET_ERROR_MESSAGE:
+ return {
+ ...state,
+ latestErrorMessage: action.data,
+ latestErrorDisplayStatus: DisplayStatus.Present,
+ };
+ case ActionTypes.HIDE_ERROR:
+ return {
+ ...state,
+ latestErrorDisplayStatus: DisplayStatus.Hidden,
+ };
+ case ActionTypes.CLEAR_ERROR:
+ return {
+ ...state,
+ latestErrorMessage: undefined,
+ latestErrorDisplayStatus: DisplayStatus.Hidden,
+ };
+ case ActionTypes.UPDATE_SELECTED_ASSET:
+ return {
+ ...state,
+ selectedAsset: action.data,
+ };
+ case ActionTypes.RESET_AMOUNT:
+ return {
+ ...state,
+ latestBuyQuote: undefined,
+ quoteRequestState: AsyncProcessState.None,
+ buyOrderState: { processState: OrderProcessState.None },
+ selectedAssetUnitAmount: undefined,
+ };
+ case ActionTypes.SET_AVAILABLE_ASSETS:
+ return {
+ ...state,
+ availableAssets: action.data,
+ };
+ case ActionTypes.OPEN_STANDARD_SLIDING_PANEL:
+ return {
+ ...state,
+ standardSlidingPanelSettings: {
+ content: action.data,
+ animationState: 'slidIn',
+ },
+ };
+ case ActionTypes.CLOSE_STANDARD_SLIDING_PANEL:
+ return {
+ ...state,
+ standardSlidingPanelSettings: {
+ content: state.standardSlidingPanelSettings.content,
+ animationState: 'slidOut',
+ },
+ };
+ default:
+ return state;
+ }
+ };
+ return reducer;
+};
+
+const reduceStateWithAccount = (state: State, account: Account) => {
+ const oldProviderState = state.providerState;
+ const newProviderState: ProviderState = {
+ ...oldProviderState,
+ account,
+ };
+ return {
+ ...state,
+ providerState: newProviderState,
+ };
+};
+
+const doesBuyQuoteMatchState = (buyQuote: BuyQuote, state: State): boolean => {
+ const selectedAssetIfExists = state.selectedAsset;
+ const selectedAssetUnitAmountIfExists = state.selectedAssetUnitAmount;
+ // if no selectedAsset or selectedAssetAmount exists on the current state, return false
+ if (_.isUndefined(selectedAssetIfExists) || _.isUndefined(selectedAssetUnitAmountIfExists)) {
+ return false;
+ }
+ // if buyQuote's assetData does not match that of the current selected asset, return false
+ if (selectedAssetIfExists.assetData !== buyQuote.assetData) {
+ return false;
+ }
+ // if ERC20 and buyQuote's assetBuyAmount does not match selectedAssetAmount, return false
+ // if ERC721, return true
+ const selectedAssetMetaData = selectedAssetIfExists.metaData;
+ if (selectedAssetMetaData.assetProxyId === AssetProxyId.ERC20) {
+ const selectedAssetAmountBaseUnits = Web3Wrapper.toBaseUnitAmount(
+ selectedAssetUnitAmountIfExists,
+ selectedAssetMetaData.decimals,
+ );
+ const doesAssetAmountMatch = selectedAssetAmountBaseUnits.eq(buyQuote.assetBuyAmount);
+ return doesAssetAmountMatch;
+ } else {
+ return true;
+ }
+};