diff --git a/common/actions/notifications.js b/common/actions/notifications.js index 8f0a8cdc..a1df1a50 100644 --- a/common/actions/notifications.js +++ b/common/actions/notifications.js @@ -2,11 +2,12 @@ /*** Shared types ***/ export type NOTIFICATION_LEVEL = 'danger' | 'warning' | 'success' | 'info'; +export type INFINITY = 'infinity'; export type Notification = { level: NOTIFICATION_LEVEL, msg: string, - duration?: number + duration?: number | INFINITY }; /*** Show Notification ***/ diff --git a/common/actions/swap.js b/common/actions/swap.js index 309a51bd..a9027669 100644 --- a/common/actions/swap.js +++ b/common/actions/swap.js @@ -1,10 +1,27 @@ // @flow - -/*** Change Step ***/ -export type ChangeStepSwapAction = { - type: 'SWAP_STEP', - value: number -}; +import type { + OriginKindSwapAction, + DestinationKindSwapAction, + OriginAmountSwapAction, + DestinationAmountSwapAction, + LoadBityRatesSucceededSwapAction, + DestinationAddressSwapAction, + BityOrderCreateSucceededSwapAction, + BityOrderCreateRequestedSwapAction, + OrderStatusSucceededSwapAction, + ChangeStepSwapAction, + Pairs, + RestartSwapAction, + LoadBityRatesRequestedSwapAction, + StopLoadBityRatesSwapAction, + BityOrderResponse, + BityOrderPostResponse, + OrderStatusRequestedSwapAction, + StopOrderTimerSwapAction, + StartOrderTimerSwapAction, + StartPollBityOrderStatusAction, + StopPollBityOrderStatusAction +} from './swapTypes'; export function changeStepSwap(value: number): ChangeStepSwapAction { return { @@ -13,25 +30,6 @@ export function changeStepSwap(value: number): ChangeStepSwapAction { }; } -/*** Change Reference Number ***/ -export type ReferenceNumberSwapAction = { - type: 'SWAP_REFERENCE_NUMBER', - value: string -}; - -export function referenceNumberSwap(value: string): ReferenceNumberSwapAction { - return { - type: 'SWAP_REFERENCE_NUMBER', - value - }; -} - -/*** Change Origin Kind ***/ -export type OriginKindSwapAction = { - type: 'SWAP_ORIGIN_KIND', - value: string -}; - export function originKindSwap(value: string): OriginKindSwapAction { return { type: 'SWAP_ORIGIN_KIND', @@ -39,12 +37,6 @@ export function originKindSwap(value: string): OriginKindSwapAction { }; } -/*** Change Destination Kind ***/ -export type DestinationKindSwapAction = { - type: 'SWAP_DESTINATION_KIND', - value: string -}; - export function destinationKindSwap(value: string): DestinationKindSwapAction { return { type: 'SWAP_DESTINATION_KIND', @@ -52,12 +44,6 @@ export function destinationKindSwap(value: string): DestinationKindSwapAction { }; } -/*** Change Origin Amount ***/ -export type OriginAmountSwapAction = { - type: 'SWAP_ORIGIN_AMOUNT', - value: ?number -}; - export function originAmountSwap(value: ?number): OriginAmountSwapAction { return { type: 'SWAP_ORIGIN_AMOUNT', @@ -65,12 +51,6 @@ export function originAmountSwap(value: ?number): OriginAmountSwapAction { }; } -/*** Change Destination Amount ***/ -export type DestinationAmountSwapAction = { - type: 'SWAP_DESTINATION_AMOUNT', - value: ?number -}; - export function destinationAmountSwap( value: ?number ): DestinationAmountSwapAction { @@ -80,32 +60,15 @@ export function destinationAmountSwap( }; } -/*** Update Bity Rates ***/ -export type Pairs = { - ETHBTC: number, - ETHREP: number, - BTCETH: number, - BTCREP: number -}; - -export type BityRatesSwapAction = { - type: 'SWAP_UPDATE_BITY_RATES', +export function loadBityRatesSucceededSwap( value: Pairs -}; - -export function updateBityRatesSwap(value: Pairs): BityRatesSwapAction { +): LoadBityRatesSucceededSwapAction { return { - type: 'SWAP_UPDATE_BITY_RATES', + type: 'SWAP_LOAD_BITY_RATES_SUCCEEDED', value }; } -/*** Change Destination Address ***/ -export type DestinationAddressSwapAction = { - type: 'SWAP_DESTINATION_ADDRESS', - value: ?string -}; - export function destinationAddressSwap( value: ?string ): DestinationAddressSwapAction { @@ -115,49 +78,92 @@ export function destinationAddressSwap( }; } -/*** Restart ***/ -export type RestartSwapAction = { - type: 'SWAP_RESTART' -}; - export function restartSwap(): RestartSwapAction { return { type: 'SWAP_RESTART' }; } -/*** Load Bity Rates ***/ -export type LoadBityRatesSwapAction = { - type: 'SWAP_LOAD_BITY_RATES' -}; - -export function loadBityRatesSwap(): LoadBityRatesSwapAction { +export function loadBityRatesRequestedSwap(): LoadBityRatesRequestedSwapAction { return { - type: 'SWAP_LOAD_BITY_RATES' + type: 'SWAP_LOAD_BITY_RATES_REQUESTED' }; } -/*** Stop Loading Bity Rates ***/ -export type StopLoadBityRatesSwapAction = { - type: 'SWAP_STOP_LOAD_BITY_RATES' -}; - export function stopLoadBityRatesSwap(): StopLoadBityRatesSwapAction { return { type: 'SWAP_STOP_LOAD_BITY_RATES' }; } -/*** Action Type Union ***/ -export type SwapAction = - | ChangeStepSwapAction - | ReferenceNumberSwapAction - | OriginKindSwapAction - | DestinationKindSwapAction - | OriginAmountSwapAction - | DestinationAmountSwapAction - | BityRatesSwapAction - | DestinationAddressSwapAction - | RestartSwapAction - | LoadBityRatesSwapAction - | StopLoadBityRatesSwapAction; +export function orderTimeSwap(value: number) { + return { + type: 'SWAP_ORDER_TIME', + value + }; +} + +export function bityOrderCreateSucceededSwap( + payload: BityOrderPostResponse +): BityOrderCreateSucceededSwapAction { + return { + type: 'SWAP_BITY_ORDER_CREATE_SUCCEEDED', + payload + }; +} + +export function bityOrderCreateRequestedSwap( + amount: number, + destinationAddress: string, + pair: string, + mode: number = 0 +): BityOrderCreateRequestedSwapAction { + return { + type: 'SWAP_ORDER_CREATE_REQUESTED', + payload: { + amount, + destinationAddress, + pair, + mode + } + }; +} + +export function orderStatusSucceededSwap( + payload: BityOrderResponse +): OrderStatusSucceededSwapAction { + return { + type: 'SWAP_BITY_ORDER_STATUS_SUCCEEDED', + payload + }; +} + +export function orderStatusRequestedSwap(): OrderStatusRequestedSwapAction { + return { + type: 'SWAP_BITY_ORDER_STATUS_REQUESTED' + }; +} + +export function startOrderTimerSwap(): StartOrderTimerSwapAction { + return { + type: 'SWAP_ORDER_START_TIMER' + }; +} + +export function stopOrderTimerSwap(): StopOrderTimerSwapAction { + return { + type: 'SWAP_ORDER_STOP_TIMER' + }; +} + +export function startPollBityOrderStatus(): StartPollBityOrderStatusAction { + return { + type: 'SWAP_START_POLL_BITY_ORDER_STATUS' + }; +} + +export function stopPollBityOrderStatus(): StopPollBityOrderStatusAction { + return { + type: 'SWAP_STOP_POLL_BITY_ORDER_STATUS' + }; +} diff --git a/common/actions/swapTypes.js b/common/actions/swapTypes.js new file mode 100644 index 00000000..c9001734 --- /dev/null +++ b/common/actions/swapTypes.js @@ -0,0 +1,128 @@ +export type Pairs = { + ETHBTC: number, + ETHREP: number, + BTCETH: number, + BTCREP: number +}; + +export type OriginKindSwapAction = { + type: 'SWAP_ORIGIN_KIND', + value: string +}; +export type DestinationKindSwapAction = { + type: 'SWAP_DESTINATION_KIND', + value: string +}; +export type OriginAmountSwapAction = { + type: 'SWAP_ORIGIN_AMOUNT', + value: ?number +}; +export type DestinationAmountSwapAction = { + type: 'SWAP_DESTINATION_AMOUNT', + value: ?number +}; +export type LoadBityRatesSucceededSwapAction = { + type: 'SWAP_LOAD_BITY_RATES_SUCCEEDED', + value: Pairs +}; +export type DestinationAddressSwapAction = { + type: 'SWAP_DESTINATION_ADDRESS', + value: ?number +}; + +export type RestartSwapAction = { + type: 'SWAP_RESTART' +}; + +export type LoadBityRatesRequestedSwapAction = { + type: 'SWAP_LOAD_BITY_RATES_REQUESTED' +}; + +export type ChangeStepSwapAction = { + type: 'SWAP_STEP', + value: number +}; + +export type StopLoadBityRatesSwapAction = { + type: 'SWAP_STOP_LOAD_BITY_RATES' +}; + +export type BityOrderCreateRequestedSwapAction = { + type: 'SWAP_ORDER_CREATE_REQUESTED', + payload: { + amount: number, + destinationAddress: string, + pair: string, + mode: number + } +}; + +type BityOrderInput = { + amount: string +}; + +type BityOrderOutput = { + amount: string +}; + +export type BityOrderResponse = { + status: string +}; + +export type BityOrderPostResponse = BityOrderResponse & { + payment_address: string, + status: string, + input: BityOrderInput, + output: BityOrderOutput, + timestamp_created: string, + validFor: number +}; + +export type BityOrderCreateSucceededSwapAction = { + type: 'SWAP_BITY_ORDER_CREATE_SUCCEEDED', + payload: BityOrderPostResponse +}; + +export type OrderStatusRequestedSwapAction = { + type: 'SWAP_BITY_ORDER_STATUS_REQUESTED', + payload: BityOrderResponse +}; + +export type OrderStatusSucceededSwapAction = { + type: 'SWAP_BITY_ORDER_STATUS_SUCCEEDED', + payload: BityOrderResponse +}; + +export type StartOrderTimerSwapAction = { + type: 'SWAP_ORDER_START_TIMER' +}; + +export type StopOrderTimerSwapAction = { + type: 'SWAP_ORDER_STOP_TIMER' +}; + +export type StartPollBityOrderStatusAction = { + type: 'SWAP_START_POLL_BITY_ORDER_STATUS' +}; + +export type StopPollBityOrderStatusAction = { + type: 'SWAP_STOP_POLL_BITY_ORDER_STATUS' +}; + +/*** Action Type Union ***/ +export type SwapAction = + | ChangeStepSwapAction + | OriginKindSwapAction + | DestinationKindSwapAction + | OriginAmountSwapAction + | DestinationAmountSwapAction + | LoadBityRatesSucceededSwapAction + | DestinationAddressSwapAction + | RestartSwapAction + | LoadBityRatesRequestedSwapAction + | StopLoadBityRatesSwapAction + | BityOrderCreateRequestedSwapAction + | BityOrderCreateSucceededSwapAction + | BityOrderResponse + | OrderStatusSucceededSwapAction + | StartPollBityOrderStatusAction; diff --git a/common/api/bity.js b/common/api/bity.js index 90e73f83..c2da47c4 100644 --- a/common/api/bity.js +++ b/common/api/bity.js @@ -1,38 +1,18 @@ // @flow import bityConfig from 'config/bity'; -import {combineAndUpper} from 'utils/formatters' +import { checkHttpStatus, parseJSON } from './utils'; +import { combineAndUpper } from 'utils/formatters'; - -function findRateFromBityRateList(rateObjects, pairName) { +function findRateFromBityRateList(rateObjects, pairName: string) { return rateObjects.find(x => x.pair === pairName); } -// FIXME better types -function _getRate(bityRates, origin: string, destination: string) { - const pairName = combineAndUpper(origin, destination); +function _getRate(bityRates, originKind: string, destinationKind: string) { + const pairName = combineAndUpper(originKind, destinationKind); const rateObjects = bityRates.objects; return findRateFromBityRateList(rateObjects, pairName); } -/** - * Gives you multiple rates from Bitys API without making multiple API calls - * @param arrayOfOriginAndDestinationDicts - [{origin: 'BTC', destination: 'ETH'}, {origin: 'BTC', destination: 'REP}] - */ -function getMultipleRates(arrayOfOriginAndDestinationDicts) { - const mappedRates = {}; - return _getAllRates().then(bityRates => { - arrayOfOriginAndDestinationDicts.forEach(each => { - const origin = each.origin; - const destination = each.destination; - const pairName = combineAndUpper(origin, destination); - const rate = _getRate(bityRates, origin, destination); - mappedRates[pairName] = parseFloat(rate.rate_we_sell); - }); - return mappedRates; - }); - // TODO - catch errors -} - export function getAllRates() { const mappedRates = {}; return _getAllRates().then(bityRates => { @@ -42,11 +22,44 @@ export function getAllRates() { }); return mappedRates; }); - // TODO - catch errors +} + +export function postOrder( + amount: number, + destAddress: string, + mode: number, + pair: string +) { + return fetch(`${bityConfig.serverURL}/order`, { + method: 'post', + body: JSON.stringify({ + amount, + destAddress, + mode, + pair + }), + headers: bityConfig.postConfig.headers + }) + .then(checkHttpStatus) + .then(parseJSON); +} + +export function getOrderStatus(orderid: string) { + return fetch(`${bityConfig.serverURL}/status`, { + method: 'POST', + body: JSON.stringify({ + orderid + }), + headers: bityConfig.postConfig.headers + }) + .then(checkHttpStatus) + .then(parseJSON); } function _getAllRates() { - return fetch(`${bityConfig.bityAPI}/v1/rate2/`).then(r => r.json()); + return fetch(`${bityConfig.bityAPI}/v1/rate2/`) + .then(checkHttpStatus) + .then(parseJSON); } -function requestStatus() {} +function requestOrderStatus() {} diff --git a/common/api/utils.js b/common/api/utils.js index f97d1908..bac14eb5 100644 --- a/common/api/utils.js +++ b/common/api/utils.js @@ -1,117 +1,13 @@ -// Request utils, -// feel free to replace with your code -// (get, post are used in ApiServices) - -import { getLocalToken } from 'api/AuthSvc'; -import config from 'config'; - -window.BASE_API = config.BASE_API; - -function requestWrapper(method) { - return async function(url, data = null, params = {}) { - if (method === 'GET') { - // is it a GET? - // GET doesn't have data - params = data; - data = null; - } else if (data === Object(data)) { - // (data === Object(data)) === _.isObject(data) - data = JSON.stringify(data); - } else { - throw new Error(`XHR invalid, check ${method} on ${url}`); - } - - // default params for fetch = method + (Content-Type) - let defaults = { - method: method, - headers: { - 'Content-Type': 'application/json; charset=UTF-8' - } - }; - - // check that req url is relative and request was sent to our domain - if (url.match(/^https?:\/\//gi) > -1) { - let token = getLocalToken(); - if (token) { - defaults.headers['Authorization'] = `JWT ${token}`; - } - url = window.BASE_API + url; - } - - if (data) { - defaults.body = data; - } - - let paramsObj = { - ...defaults, - headers: { ...params, ...defaults.headers } - }; - - return await fetch(url, paramsObj).then(parseJSON).catch(err => { - console.error(err); - }); - }; -} - -// middlewares -// parse fetch json, add ok property and return request result - -/** - * 1. parse response - * 2. add "ok" property to result - * 3. return request result - * @param {Object} res - response from server - * @return {Object} response result with "ok" property - */ -async function parseJSON(res) { - let json; - try { - json = await res.json(); - } catch (e) { - return { data: {}, ok: false }; - } - - // simplest validation ever, ahah :) - if (!res.ok) { - return { data: json, ok: false }; - } - // resultOK - is a function with side effects - // It removes ok property from result object - return { data: json, ok: true }; -} - -export const get = requestWrapper('GET'); -export const post = requestWrapper('POST'); -export const put = requestWrapper('PUT'); -export const patch = requestWrapper('PATCH'); -export const del = requestWrapper('DELETE'); - -// USAGE: -// get('https://www.google.com', { -// Authorization: 'JWT LOL', -// headers: { -// 'Content-Type': 'text/html' -// } -// }) - -// FUNCTION WITH SIDE-EFFECTS -/** - * `parseJSON()` adds property "ok" - * that identicates that response is OK - * - * `resultOK`removes result.ok from result and returns "ok" property - * It widely used in `/actions/*` - * for choosing action to dispatch after request to API - * - * @param {Object} result - response result that - * @return {bool} - indicates was request successful or not - */ -export function resultOK(result) { - if (result) { - let ok = result.ok; - delete result.ok; - return ok; //look at parseJSON +export function checkHttpStatus(response) { + if (response.status >= 200 && response.status < 300) { + return response; } else { - return false; + let error = new Error(response.statusText); + error.response = response; + throw error; } } + +export function parseJSON(response) { + return response.json(); +} diff --git a/common/components/Header/components/Navigation.jsx b/common/components/Header/components/Navigation.jsx index a51c3a00..b704f421 100644 --- a/common/components/Header/components/Navigation.jsx +++ b/common/components/Header/components/Navigation.jsx @@ -74,10 +74,11 @@ export default class TabsOptions extends Component {