/ common / features / swap / reducer.ts
reducer.ts
  1  import { schema, normalize } from 'normalizr';
  2  
  3  import * as types from './types';
  4  
  5  export const allIds = (byIds: { [name: string]: {} }) => {
  6    return Object.keys(byIds);
  7  };
  8  
  9  export const option = new schema.Entity('options');
 10  export const providerRate = new schema.Entity('providerRates', {
 11    options: [option]
 12  });
 13  
 14  export const INITIAL_STATE: types.SwapState = {
 15    step: 1,
 16    origin: { label: 'BTC', amount: NaN },
 17    destination: { label: 'ETH', amount: NaN },
 18    options: {
 19      byId: {},
 20      allIds: []
 21    },
 22    bityRates: {
 23      byId: {},
 24      allIds: []
 25    },
 26    shapeshiftRates: {
 27      byId: {},
 28      allIds: []
 29    },
 30    provider: 'shapeshift',
 31    destinationAddress: '',
 32    bityOrder: {},
 33    shapeshiftOrder: {},
 34    isFetchingRates: null,
 35    hasNotifiedRatesFailure: false,
 36    secondsRemaining: null,
 37    outputTx: null,
 38    isPostingOrder: false,
 39    bityOrderStatus: null,
 40    shapeshiftOrderStatus: null,
 41    orderTimestampCreatedISOString: null,
 42    paymentAddress: null,
 43    validFor: null,
 44    orderId: null,
 45    showLiteSend: false,
 46    paymentId: null,
 47    xmrPaymentAddress: null
 48  };
 49  
 50  export function swapReducer(state: types.SwapState = INITIAL_STATE, action: types.SwapAction) {
 51    switch (action.type) {
 52      case types.SwapActions.LOAD_BITY_RATES_SUCCEEDED:
 53        const { payload } = action;
 54        return {
 55          ...state,
 56          bityRates: {
 57            byId: normalize(payload, [providerRate]).entities.providerRates,
 58            allIds: allIds(normalize(payload, [providerRate]).entities.providerRates)
 59          },
 60          options: {
 61            byId: Object.assign(
 62              {},
 63              normalize(payload, [providerRate]).entities.options,
 64              state.options.byId
 65            ),
 66            allIds: [
 67              ...allIds(normalize(payload, [providerRate]).entities.options),
 68              ...state.options.allIds
 69            ]
 70          },
 71          isFetchingRates: false
 72        };
 73      case types.SwapActions.LOAD_SHAPESHIFT_RATES_SUCCEEDED:
 74        const {
 75          entities: { providerRates: normalizedProviderRates, options: normalizedOptions }
 76        } = normalize(action.payload, [providerRate]);
 77  
 78        return {
 79          ...state,
 80          shapeshiftRates: {
 81            byId: normalizedProviderRates,
 82            allIds: allIds(normalizedProviderRates)
 83          },
 84          options: {
 85            byId: { ...normalizedOptions, ...state.options.byId },
 86            allIds: [...allIds(normalizedOptions), ...state.options.allIds]
 87          },
 88          isFetchingRates: false
 89        };
 90      case types.SwapActions.INIT: {
 91        return {
 92          ...state,
 93          origin: action.payload.origin,
 94          destination: action.payload.destination
 95        };
 96      }
 97      case types.SwapActions.STEP: {
 98        return {
 99          ...state,
100          step: action.payload
101        };
102      }
103      case types.SwapActions.DESTINATION_ADDRESS:
104        return {
105          ...state,
106          destinationAddress: action.payload
107        };
108      case types.SwapActions.RESTART:
109        return {
110          ...INITIAL_STATE,
111          options: state.options,
112          bityRates: state.bityRates,
113          shapeshiftRates: state.shapeshiftRates
114        };
115      case types.SwapActions.BITY_ORDER_CREATE_REQUESTED:
116        return {
117          ...state,
118          isPostingOrder: true
119        };
120      case types.SwapActions.SHAPESHIFT_ORDER_CREATE_REQUESTED:
121        return {
122          ...state,
123          isPostingOrder: true
124        };
125      case types.SwapActions.BITY_ORDER_CREATE_FAILED:
126        return {
127          ...state,
128          isPostingOrder: false
129        };
130      case types.SwapActions.SHAPESHIFT_ORDER_CREATE_FAILED:
131        return {
132          ...state,
133          isPostingOrder: false
134        };
135      case types.SwapActions.BITY_ORDER_CREATE_SUCCEEDED:
136        return {
137          ...state,
138          bityOrder: {
139            ...action.payload
140          },
141          isPostingOrder: false,
142          originAmount: parseFloat(action.payload.input.amount),
143          destinationAmount: parseFloat(action.payload.output.amount),
144          secondsRemaining: action.payload.validFor, // will get update
145          validFor: action.payload.validFor, // to build from local storage
146          orderTimestampCreatedISOString: action.payload.timestamp_created,
147          paymentAddress: action.payload.payment_address,
148          bityOrderStatus: action.payload.status,
149          orderId: action.payload.id
150        };
151      case types.SwapActions.SHAPESHIFT_ORDER_CREATE_SUCCEEDED:
152        const currDate = Date.now();
153        const secondsRemaining = Math.floor((+new Date(action.payload.expiration) - currDate) / 1000);
154  
155        return {
156          ...state,
157          shapeshiftOrder: {
158            ...action.payload
159          },
160          isPostingOrder: false,
161          originAmount: parseFloat(action.payload.depositAmount),
162          destinationAmount: parseFloat(action.payload.withdrawalAmount),
163          secondsRemaining,
164          validFor: secondsRemaining,
165          orderTimestampCreatedISOString: new Date(currDate).toISOString(),
166          paymentAddress: action.payload.deposit,
167          shapeshiftOrderStatus: 'no_deposits',
168          orderId: action.payload.orderId,
169          // For XMR swaps
170          paymentId: action.payload.deposit,
171          xmrPaymentAddress: action.payload.sAddress
172        };
173      case types.SwapActions.BITY_ORDER_STATUS_SUCCEEDED:
174        return {
175          ...state,
176          outputTx: action.payload.output.reference,
177          bityOrderStatus:
178            action.payload.output.status === 'FILL'
179              ? action.payload.output.status
180              : action.payload.input.status
181        };
182      case types.SwapActions.SHAPESHIFT_ORDER_STATUS_SUCCEEDED:
183        return {
184          ...state,
185          outputTx: action.payload && action.payload.transaction ? action.payload.transaction : null,
186          shapeshiftOrderStatus: action.payload.status
187        };
188      case types.SwapActions.ORDER_TIME:
189        return {
190          ...state,
191          secondsRemaining: action.payload
192        };
193  
194      case types.SwapActions.LOAD_BITY_RATES_REQUESTED:
195      case types.SwapActions.LOAD_SHAPESHIFT_RATES_REQUESTED:
196        return {
197          ...state,
198          isFetchingRates: true,
199          hasNotifiedRatesFailure: false
200        };
201      case types.SwapActions.LOAD_BITY_RATES_FAILED:
202      case types.SwapActions.LOAD_SHAPESHIFT_RATES_FAILED:
203        return {
204          ...state,
205          hasNotifiedRatesFailure: true
206        };
207      case types.SwapActions.STOP_LOAD_BITY_RATES:
208        return {
209          ...state,
210          isFetchingRates: false
211        };
212      case types.SwapActions.STOP_LOAD_SHAPESHIFT_RATES:
213        return {
214          ...state,
215          isFetchingRates: false
216        };
217      case types.SwapActions.CHANGE_PROVIDER:
218        return {
219          ...state,
220          provider: action.payload
221        };
222      case types.SwapActions.SHOW_LITE_SEND:
223        return {
224          ...state,
225          showLiteSend: action.payload
226        };
227      default:
228        return state;
229    }
230  }