aboutsummaryrefslogblamecommitdiffstats
path: root/packages/website/ts/redux/reducer.ts
blob: 7723597cdf74c443ce9cb26f5c03aca28255bad8 (plain) (tree)










































































































































































































































































































































































                                                                                                 
import * as _ from 'lodash';
import {ZeroEx} from '0x.js';
import BigNumber from 'bignumber.js';
import {utils} from 'ts/utils/utils';
import {
    GenerateOrderSteps,
    Side,
    SideToAssetToken,
    Direction,
    BlockchainErrs,
    SignatureData,
    TokenByAddress,
    TokenStateByAddress,
    Order,
    Action,
    ActionTypes,
    ScreenWidths,
    ProviderType,
    TokenState,
} from 'ts/types';

// Instead of defaulting the docs version to an empty string, we pre-populate it with
// a valid version value. This does not need to be updated however, since onLoad, it
// is always replaced with a value retrieved from our S3 bucket.
const DEFAULT_DOCS_VERSION = '0.0.0';

export interface State {
    // Portal
    blockchainErr: BlockchainErrs;
    blockchainIsLoaded: boolean;
    generateOrderStep: GenerateOrderSteps;
    networkId: number;
    orderExpiryTimestamp: BigNumber;
    orderFillAmount: BigNumber;
    orderTakerAddress: string;
    orderSignatureData: SignatureData;
    orderSalt: BigNumber;
    nodeVersion: string;
    screenWidth: ScreenWidths;
    shouldBlockchainErrDialogBeOpen: boolean;
    sideToAssetToken: SideToAssetToken;
    tokenByAddress: TokenByAddress;
    tokenStateByAddress: TokenStateByAddress;
    userAddress: string;
    userEtherBalance: BigNumber;
    // Note: cache of supplied orderJSON in fill order step. Do not use for anything else.
    userSuppliedOrderCache: Order;

    // Docs
    docsVersion: string;
    availableDocVersions: string[];

    // Shared
    flashMessage: string|React.ReactNode;
    providerType: ProviderType;
    injectedProviderName: string;
};

const INITIAL_STATE: State = {
    // Portal
    blockchainErr: '',
    blockchainIsLoaded: false,
    generateOrderStep: GenerateOrderSteps.ChooseAssets,
    networkId: undefined,
    orderExpiryTimestamp: utils.initialOrderExpiryUnixTimestampSec(),
    orderFillAmount: undefined,
    orderSignatureData: {
        hash: '',
        r: '',
        s: '',
        v: 27,
    },
    orderTakerAddress: '',
    orderSalt: ZeroEx.generatePseudoRandomSalt(),
    nodeVersion: undefined,
    screenWidth: utils.getScreenWidth(),
    shouldBlockchainErrDialogBeOpen: false,
    sideToAssetToken: {
        [Side.deposit]: {},
        [Side.receive]: {},
    },
    tokenByAddress: {},
    tokenStateByAddress: {},
    userAddress: '',
    userEtherBalance: new BigNumber(0),
    userSuppliedOrderCache: undefined,

    // Docs
    docsVersion: DEFAULT_DOCS_VERSION,
    availableDocVersions: [DEFAULT_DOCS_VERSION],

    // Shared
    flashMessage: undefined,
    providerType: ProviderType.INJECTED,
    injectedProviderName: '',
};

export function reducer(state: State = INITIAL_STATE, action: Action) {
    switch (action.type) {
        // Portal
        case ActionTypes.RESET_STATE:
            return INITIAL_STATE;

        case ActionTypes.UPDATE_ORDER_SALT: {
            return _.assign({}, state, {
                orderSalt: action.data,
            });
        }

        case ActionTypes.UPDATE_NODE_VERSION: {
            return _.assign({}, state, {
                nodeVersion: action.data,
            });
        }

        case ActionTypes.UPDATE_ORDER_FILL_AMOUNT: {
            return _.assign({}, state, {
                orderFillAmount: action.data,
            });
        }

        case ActionTypes.UPDATE_SHOULD_BLOCKCHAIN_ERR_DIALOG_BE_OPEN: {
            return _.assign({}, state, {
                shouldBlockchainErrDialogBeOpen: action.data,
            });
        }

        case ActionTypes.UPDATE_USER_ETHER_BALANCE: {
            return _.assign({}, state, {
                userEtherBalance: action.data,
            });
        }

        case ActionTypes.UPDATE_USER_SUPPLIED_ORDER_CACHE: {
            return _.assign({}, state, {
                userSuppliedOrderCache: action.data,
            });
        }

        case ActionTypes.CLEAR_TOKEN_BY_ADDRESS: {
            return _.assign({}, state, {
                tokenByAddress: {},
            });
        }

        case ActionTypes.ADD_TOKEN_TO_TOKEN_BY_ADDRESS: {
            const newTokenByAddress = state.tokenByAddress;
            newTokenByAddress[action.data.address] = action.data;
            return _.assign({}, state, {
                tokenByAddress: newTokenByAddress,
            });
        }

        case ActionTypes.REMOVE_TOKEN_TO_TOKEN_BY_ADDRESS: {
            const newTokenByAddress = state.tokenByAddress;
            delete newTokenByAddress[action.data.address];
            return _.assign({}, state, {
                tokenByAddress: newTokenByAddress,
            });
        }

        case ActionTypes.UPDATE_TOKEN_BY_ADDRESS: {
            const tokenByAddress = state.tokenByAddress;
            const tokens = action.data;
            _.each(tokens, token => {
                const updatedToken = _.assign({}, tokenByAddress[token.address], token);
                tokenByAddress[token.address] = updatedToken;
            });
            return _.assign({}, state, {
                tokenByAddress,
            });
        }

        case ActionTypes.UPDATE_TOKEN_STATE_BY_ADDRESS: {
            const tokenStateByAddress = state.tokenStateByAddress;
            const updatedTokenStateByAddress = action.data;
            _.each(updatedTokenStateByAddress, (tokenState: TokenState, address: string) => {
                const updatedTokenState = _.assign({}, tokenStateByAddress[address], tokenState);
                tokenStateByAddress[address] = updatedTokenState;
            });
            return _.assign({}, state, {
                tokenStateByAddress,
            });
        }

        case ActionTypes.REMOVE_FROM_TOKEN_STATE_BY_ADDRESS: {
            const tokenStateByAddress = state.tokenStateByAddress;
            const tokenAddress = action.data;
            delete tokenStateByAddress[tokenAddress];
            return _.assign({}, state, {
                tokenStateByAddress,
            });
        }

        case ActionTypes.REPLACE_TOKEN_ALLOWANCE_BY_ADDRESS: {
            const tokenStateByAddress = state.tokenStateByAddress;
            const allowance = action.data.allowance;
            const tokenAddress = action.data.address;
            tokenStateByAddress[tokenAddress] = _.assign({}, tokenStateByAddress[tokenAddress], {
                allowance,
            });
            return _.assign({}, state, {
                tokenStateByAddress,
            });
        }

        case ActionTypes.REPLACE_TOKEN_BALANCE_BY_ADDRESS: {
            const tokenStateByAddress = state.tokenStateByAddress;
            const balance = action.data.balance;
            const tokenAddress = action.data.address;
            tokenStateByAddress[tokenAddress] = _.assign({}, tokenStateByAddress[tokenAddress], {
                balance,
            });
            return _.assign({}, state, {
                tokenStateByAddress,
            });
        }

        case ActionTypes.UPDATE_TOKEN_BALANCE_BY_ADDRESS: {
            const tokenStateByAddress = state.tokenStateByAddress;
            const balanceDelta = action.data.balanceDelta;
            const tokenAddress = action.data.address;
            const currBalance = tokenStateByAddress[tokenAddress].balance;
            tokenStateByAddress[tokenAddress] = _.assign({}, tokenStateByAddress[tokenAddress], {
                balance: currBalance.plus(balanceDelta),
            });
            return _.assign({}, state, {
                tokenStateByAddress,
            });
        }

        case ActionTypes.UPDATE_ORDER_SIGNATURE_DATA: {
            return _.assign({}, state, {
                orderSignatureData: action.data,
            });
        }

        case ActionTypes.UPDATE_SCREEN_WIDTH: {
            return _.assign({}, state, {
                screenWidth: action.data,
            });
        }

        case ActionTypes.UPDATE_BLOCKCHAIN_IS_LOADED: {
            return _.assign({}, state, {
                blockchainIsLoaded: action.data,
            });
        }

        case ActionTypes.BLOCKCHAIN_ERR_ENCOUNTERED: {
            return _.assign({}, state, {
                blockchainErr: action.data,
            });
        }

        case ActionTypes.UPDATE_NETWORK_ID: {
            return _.assign({}, state, {
                networkId: action.data,
            });
        }

        case ActionTypes.UPDATE_GENERATE_ORDER_STEP: {
            const direction = action.data;
            let nextGenerateOrderStep = state.generateOrderStep;
            if (direction === Direction.forward) {
                nextGenerateOrderStep += 1;
            } else if (state.generateOrderStep !== 0) {
                nextGenerateOrderStep -= 1;
            }
            return _.assign({}, state, {
                generateOrderStep: nextGenerateOrderStep,
            });
        }

        case ActionTypes.UPDATE_CHOSEN_ASSET_TOKEN: {
            const newSideToAssetToken = _.assign({}, state.sideToAssetToken, {
                [action.data.side]: action.data.token,
            });
            return _.assign({}, state, {
                sideToAssetToken: newSideToAssetToken,
            });
        }

        case ActionTypes.UPDATE_CHOSEN_ASSET_TOKEN_ADDRESS: {
            const newAssetToken = state.sideToAssetToken[action.data.side];
            newAssetToken.address = action.data.address;
            const newSideToAssetToken = _.assign({}, state.sideToAssetToken, {
                [action.data.side]: newAssetToken,
            });
            return _.assign({}, state, {
                sideToAssetToken: newSideToAssetToken,
            });
        }

        case ActionTypes.SWAP_ASSET_TOKENS: {
            const newSideToAssetToken = _.assign({}, state.sideToAssetToken, {
                [Side.deposit]: state.sideToAssetToken[Side.receive],
                [Side.receive]: state.sideToAssetToken[Side.deposit],
            });
            return _.assign({}, state, {
                sideToAssetToken: newSideToAssetToken,
            });
        }

        case ActionTypes.UPDATE_ORDER_EXPIRY: {
            return _.assign({}, state, {
                orderExpiryTimestamp: action.data,
            });
        }

        case ActionTypes.UPDATE_ORDER_TAKER_ADDRESS: {
            return _.assign({}, state, {
                orderTakerAddress: action.data,
            });
        }

        case ActionTypes.UPDATE_USER_ADDRESS: {
            return _.assign({}, state, {
                userAddress: action.data,
            });
        }

        // Docs
        case ActionTypes.UPDATE_LIBRARY_VERSION: {
            return _.assign({}, state, {
                docsVersion: action.data,
            });
        }
        case ActionTypes.UPDATE_AVAILABLE_LIBRARY_VERSIONS: {
            return _.assign({}, state, {
                availableDocVersions: action.data,
            });
        }

        // Shared
        case ActionTypes.SHOW_FLASH_MESSAGE: {
            return _.assign({}, state, {
                flashMessage: action.data,
            });
        }

        case ActionTypes.HIDE_FLASH_MESSAGE: {
            return _.assign({}, state, {
                flashMessage: undefined,
            });
        }

        case ActionTypes.UPDATE_PROVIDER_TYPE: {
            return _.assign({}, state, {
                providerType: action.data,
            });
        }

        case ActionTypes.UPDATE_INJECTED_PROVIDER_NAME: {
            return _.assign({}, state, {
                injectedProviderName: action.data,
            });
        }

        default:
            return state;
    }
}