Minor swap rates refactor (#1162)
* Refactor swaps: * Remove references to bity fallback, it aint happening * Consolidate sagas to single orchestrator function like other sagas * Grab rates once, dont continuously poll * tscheck * Re-instate the auto-fetching behavior. This time, only notify the first time. * Make CurrentRates responsible for redux actions. Fix up some typings. * Remove commented code. * Update snapshot.
This commit is contained in:
parent
b48617e95e
commit
33f5ede22a
|
@ -27,10 +27,10 @@ export function loadBityRatesSucceededSwap(
|
|||
};
|
||||
}
|
||||
|
||||
export type TLoadShapeshiftSucceededSwap = typeof loadShapeshiftRatesSucceededSwap;
|
||||
export type TLoadShapeshiftRatesSucceededSwap = typeof loadShapeshiftRatesSucceededSwap;
|
||||
export function loadShapeshiftRatesSucceededSwap(
|
||||
payload
|
||||
): interfaces.LoadShapshiftRatesSucceededSwapAction {
|
||||
): interfaces.LoadShapeshiftRatesSucceededSwapAction {
|
||||
return {
|
||||
type: TypeKeys.SWAP_LOAD_SHAPESHIFT_RATES_SUCCEEDED,
|
||||
payload
|
||||
|
@ -59,13 +59,27 @@ export function loadBityRatesRequestedSwap(): interfaces.LoadBityRatesRequestedS
|
|||
};
|
||||
}
|
||||
|
||||
export type TLoadShapeshiftRequestedSwap = typeof loadShapeshiftRatesRequestedSwap;
|
||||
export function loadShapeshiftRatesRequestedSwap(): interfaces.LoadShapeshiftRequestedSwapAction {
|
||||
export type TLoadShapeshiftRatesRequestedSwap = typeof loadShapeshiftRatesRequestedSwap;
|
||||
export function loadShapeshiftRatesRequestedSwap(): interfaces.LoadShapeshiftRatesRequestedSwapAction {
|
||||
return {
|
||||
type: TypeKeys.SWAP_LOAD_SHAPESHIFT_RATES_REQUESTED
|
||||
};
|
||||
}
|
||||
|
||||
export type TLoadBityRatesFailedSwap = typeof loadBityRatesFailedSwap;
|
||||
export function loadBityRatesFailedSwap(): interfaces.LoadBityRatesFailedSwapAction {
|
||||
return {
|
||||
type: TypeKeys.SWAP_LOAD_BITY_RATES_FAILED
|
||||
};
|
||||
}
|
||||
|
||||
export type TLoadShapeshiftFailedSwap = typeof loadShapeshiftRatesFailedSwap;
|
||||
export function loadShapeshiftRatesFailedSwap(): interfaces.LoadShapeshiftRatesFailedSwapAction {
|
||||
return {
|
||||
type: TypeKeys.SWAP_LOAD_SHAPESHIFT_RATES_FAILED
|
||||
};
|
||||
}
|
||||
|
||||
export type TStopLoadBityRatesSwap = typeof stopLoadBityRatesSwap;
|
||||
export function stopLoadBityRatesSwap(): interfaces.StopLoadBityRatesSwapAction {
|
||||
return {
|
||||
|
|
|
@ -43,7 +43,7 @@ export interface LoadBityRatesSucceededSwapAction {
|
|||
payload: ApiResponse;
|
||||
}
|
||||
|
||||
export interface LoadShapshiftRatesSucceededSwapAction {
|
||||
export interface LoadShapeshiftRatesSucceededSwapAction {
|
||||
type: TypeKeys.SWAP_LOAD_SHAPESHIFT_RATES_SUCCEEDED;
|
||||
payload: ApiResponse;
|
||||
}
|
||||
|
@ -59,12 +59,18 @@ export interface RestartSwapAction {
|
|||
|
||||
export interface LoadBityRatesRequestedSwapAction {
|
||||
type: TypeKeys.SWAP_LOAD_BITY_RATES_REQUESTED;
|
||||
payload?: null;
|
||||
}
|
||||
|
||||
export interface LoadShapeshiftRequestedSwapAction {
|
||||
export interface LoadShapeshiftRatesRequestedSwapAction {
|
||||
type: TypeKeys.SWAP_LOAD_SHAPESHIFT_RATES_REQUESTED;
|
||||
payload?: null;
|
||||
}
|
||||
|
||||
export interface LoadBityRatesFailedSwapAction {
|
||||
type: TypeKeys.SWAP_LOAD_BITY_RATES_FAILED;
|
||||
}
|
||||
|
||||
export interface LoadShapeshiftRatesFailedSwapAction {
|
||||
type: TypeKeys.SWAP_LOAD_SHAPESHIFT_RATES_FAILED;
|
||||
}
|
||||
|
||||
export interface ChangeStepSwapAction {
|
||||
|
@ -240,12 +246,14 @@ export interface ShowLiteSendAction {
|
|||
export type SwapAction =
|
||||
| ChangeStepSwapAction
|
||||
| InitSwap
|
||||
| LoadBityRatesSucceededSwapAction
|
||||
| LoadShapshiftRatesSucceededSwapAction
|
||||
| DestinationAddressSwapAction
|
||||
| RestartSwapAction
|
||||
| LoadBityRatesRequestedSwapAction
|
||||
| LoadShapeshiftRequestedSwapAction
|
||||
| LoadBityRatesSucceededSwapAction
|
||||
| LoadBityRatesFailedSwapAction
|
||||
| LoadShapeshiftRatesRequestedSwapAction
|
||||
| LoadShapeshiftRatesSucceededSwapAction
|
||||
| LoadShapeshiftRatesFailedSwapAction
|
||||
| StopLoadBityRatesSwapAction
|
||||
| StopLoadShapeshiftRatesSwapAction
|
||||
| BityOrderCreateRequestedSwapAction
|
||||
|
|
|
@ -7,6 +7,8 @@ export enum TypeKeys {
|
|||
SWAP_RESTART = 'SWAP_RESTART',
|
||||
SWAP_LOAD_BITY_RATES_REQUESTED = 'SWAP_LOAD_BITY_RATES_REQUESTED',
|
||||
SWAP_LOAD_SHAPESHIFT_RATES_REQUESTED = 'SWAP_LOAD_SHAPESHIFT_RATES_REQUESTED',
|
||||
SWAP_LOAD_BITY_RATES_FAILED = 'SWAP_LOAD_BITY_RATES_FAILED',
|
||||
SWAP_LOAD_SHAPESHIFT_RATES_FAILED = 'SWAP_LOAD_SHAPESHIFT_RATES_FAILED',
|
||||
SWAP_STOP_LOAD_BITY_RATES = 'SWAP_STOP_LOAD_BITY_RATES',
|
||||
SWAP_STOP_LOAD_SHAPESHIFT_RATES = 'SWAP_STOP_LOAD_SHAPESHIFT_RATES',
|
||||
SWAP_ORDER_TIME = 'SWAP_ORDER_TIME',
|
||||
|
|
|
@ -85,8 +85,9 @@ export default class CurrencySwap extends PureComponent<Props, State> {
|
|||
this.setErrorMessages(originErr, destinationErr);
|
||||
}, 1000);
|
||||
|
||||
private timeoutId: NodeJS.Timer | null;
|
||||
public componentDidMount() {
|
||||
setTimeout(() => {
|
||||
this.timeoutId = setTimeout(() => {
|
||||
this.setState({
|
||||
timeout: true
|
||||
});
|
||||
|
@ -108,6 +109,12 @@ export default class CurrencySwap extends PureComponent<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
if (this.timeoutId) {
|
||||
clearTimeout(this.timeoutId);
|
||||
}
|
||||
}
|
||||
|
||||
public componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
const { origin, destination } = this.state;
|
||||
const { options } = this.props;
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
import React, { PureComponent } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import sample from 'lodash/sample';
|
||||
import times from 'lodash/times';
|
||||
import {
|
||||
NormalizedBityRates,
|
||||
NormalizedShapeshiftRates,
|
||||
|
@ -7,24 +11,57 @@ import bityLogoWhite from 'assets/images/logo-bity-white.svg';
|
|||
import shapeshiftLogoWhite from 'assets/images/logo-shapeshift.svg';
|
||||
import Spinner from 'components/ui/Spinner';
|
||||
import { bityReferralURL, shapeshiftReferralURL } from 'config';
|
||||
import React, { PureComponent } from 'react';
|
||||
import translate from 'translations';
|
||||
import './CurrentRates.scss';
|
||||
import { SHAPESHIFT_WHITELIST } from 'api/shapeshift';
|
||||
import { ProviderName } from 'actions/swap';
|
||||
import sample from 'lodash/sample';
|
||||
import times from 'lodash/times';
|
||||
import {
|
||||
loadShapeshiftRatesRequestedSwap,
|
||||
TLoadShapeshiftRatesRequestedSwap,
|
||||
stopLoadShapeshiftRatesSwap,
|
||||
TStopLoadShapeshiftRatesSwap,
|
||||
ProviderName
|
||||
} from 'actions/swap';
|
||||
import { getOffline } from 'selectors/config';
|
||||
import Rates from './Rates';
|
||||
import { AppState } from 'reducers';
|
||||
import './CurrentRates.scss';
|
||||
|
||||
interface Props {
|
||||
interface StateProps {
|
||||
isOffline: boolean;
|
||||
provider: ProviderName;
|
||||
bityRates: NormalizedBityRates;
|
||||
shapeshiftRates: NormalizedShapeshiftRates;
|
||||
}
|
||||
|
||||
export default class CurrentRates extends PureComponent<Props> {
|
||||
interface ActionProps {
|
||||
loadShapeshiftRatesRequestedSwap: TLoadShapeshiftRatesRequestedSwap;
|
||||
stopLoadShapeshiftRatesSwap: TStopLoadShapeshiftRatesSwap;
|
||||
}
|
||||
|
||||
type Props = StateProps & ActionProps;
|
||||
|
||||
class CurrentRates extends PureComponent<Props> {
|
||||
private shapeShiftRateCache = null;
|
||||
|
||||
public componentDidMount() {
|
||||
if (!this.props.isOffline) {
|
||||
this.loadRates();
|
||||
}
|
||||
}
|
||||
|
||||
public componentWillReceiveProps(nextProps: Props) {
|
||||
if (this.props.isOffline && !nextProps.isOffline) {
|
||||
this.loadRates();
|
||||
}
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
this.props.stopLoadShapeshiftRatesSwap();
|
||||
}
|
||||
|
||||
public loadRates() {
|
||||
this.props.loadShapeshiftRatesRequestedSwap();
|
||||
}
|
||||
|
||||
public getRandomSSPairData = (
|
||||
shapeshiftRates: NormalizedShapeshiftRates
|
||||
): NormalizedShapeshiftRate => {
|
||||
|
@ -139,3 +176,17 @@ export default class CurrentRates extends PureComponent<Props> {
|
|||
return this.swapEl(providerURL, providerLogo, children);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state: AppState): StateProps {
|
||||
return {
|
||||
isOffline: getOffline(state),
|
||||
provider: state.swap.provider,
|
||||
bityRates: state.swap.bityRates,
|
||||
shapeshiftRates: state.swap.shapeshiftRates
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, {
|
||||
loadShapeshiftRatesRequestedSwap,
|
||||
stopLoadShapeshiftRatesSwap
|
||||
})(CurrentRates);
|
||||
|
|
|
@ -5,8 +5,6 @@ import {
|
|||
shapeshiftOrderCreateRequestedSwap as dShapeshiftOrderCreateRequestedSwap,
|
||||
changeStepSwap as dChangeStepSwap,
|
||||
destinationAddressSwap as dDestinationAddressSwap,
|
||||
loadBityRatesRequestedSwap as dLoadBityRatesRequestedSwap,
|
||||
loadShapeshiftRatesRequestedSwap as dLoadShapeshiftRatesRequestedSwap,
|
||||
restartSwap as dRestartSwap,
|
||||
startOrderTimerSwap as dStartOrderTimerSwap,
|
||||
startPollBityOrderStatus as dStartPollBityOrderStatus,
|
||||
|
@ -21,9 +19,7 @@ import {
|
|||
TBityOrderCreateRequestedSwap,
|
||||
TChangeStepSwap,
|
||||
TDestinationAddressSwap,
|
||||
TLoadBityRatesRequestedSwap,
|
||||
TShapeshiftOrderCreateRequestedSwap,
|
||||
TLoadShapeshiftRequestedSwap,
|
||||
TRestartSwap,
|
||||
TStartOrderTimerSwap,
|
||||
TStartPollBityOrderStatus,
|
||||
|
@ -81,8 +77,6 @@ interface ReduxStateProps {
|
|||
|
||||
interface ReduxActionProps {
|
||||
changeStepSwap: TChangeStepSwap;
|
||||
loadBityRatesRequestedSwap: TLoadBityRatesRequestedSwap;
|
||||
loadShapeshiftRatesRequestedSwap: TLoadShapeshiftRequestedSwap;
|
||||
destinationAddressSwap: TDestinationAddressSwap;
|
||||
restartSwap: TRestartSwap;
|
||||
stopLoadBityRatesSwap: TStopLoadBityRatesSwap;
|
||||
|
@ -101,26 +95,6 @@ interface ReduxActionProps {
|
|||
}
|
||||
|
||||
class Swap extends Component<ReduxActionProps & ReduxStateProps & RouteComponentProps<{}>, {}> {
|
||||
public componentDidMount() {
|
||||
if (!this.props.isOffline) {
|
||||
this.loadRates();
|
||||
}
|
||||
}
|
||||
|
||||
public componentWillReceiveProps(nextProps: ReduxStateProps) {
|
||||
if (this.props.isOffline && !nextProps.isOffline) {
|
||||
this.loadRates();
|
||||
}
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
this.props.stopLoadShapeshiftRatesSwap();
|
||||
}
|
||||
|
||||
public loadRates() {
|
||||
this.props.loadShapeshiftRatesRequestedSwap();
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {
|
||||
// STATE
|
||||
|
@ -235,8 +209,6 @@ class Swap extends Component<ReduxActionProps & ReduxStateProps & RouteComponent
|
|||
bityRates
|
||||
};
|
||||
|
||||
const CurrentRatesProps = { provider, bityRates, shapeshiftRates };
|
||||
|
||||
return (
|
||||
<TabSection isUnavailableOffline={true}>
|
||||
<section className="Tab-content swap-tab">
|
||||
|
@ -246,7 +218,7 @@ class Swap extends Component<ReduxActionProps & ReduxStateProps & RouteComponent
|
|||
path={`${currentPath}`}
|
||||
render={() => (
|
||||
<React.Fragment>
|
||||
{step === 1 && <CurrentRates {...CurrentRatesProps} />}
|
||||
{step === 1 && <CurrentRates />}
|
||||
{step === 1 && <ShapeshiftBanner />}
|
||||
{(step === 2 || step === 3) && <SwapInfoHeader {...SwapInfoHeaderProps} />}
|
||||
<main className="Tab-content-pane">
|
||||
|
@ -294,8 +266,6 @@ export default connect(mapStateToProps, {
|
|||
initSwap: dInitSwap,
|
||||
bityOrderCreateRequestedSwap: dBityOrderCreateRequestedSwap,
|
||||
shapeshiftOrderCreateRequestedSwap: dShapeshiftOrderCreateRequestedSwap,
|
||||
loadBityRatesRequestedSwap: dLoadBityRatesRequestedSwap,
|
||||
loadShapeshiftRatesRequestedSwap: dLoadShapeshiftRatesRequestedSwap,
|
||||
destinationAddressSwap: dDestinationAddressSwap,
|
||||
restartSwap: dRestartSwap,
|
||||
startOrderTimerSwap: dStartOrderTimerSwap,
|
||||
|
|
|
@ -11,12 +11,13 @@ export interface State {
|
|||
options: stateTypes.NormalizedOptions;
|
||||
bityRates: stateTypes.NormalizedBityRates;
|
||||
// Change this
|
||||
shapeshiftRates: stateTypes.NormalizedBityRates;
|
||||
provider: string;
|
||||
shapeshiftRates: stateTypes.NormalizedShapeshiftRates;
|
||||
provider: stateTypes.ProviderName;
|
||||
bityOrder: any;
|
||||
shapeshiftOrder: any;
|
||||
destinationAddress: string;
|
||||
isFetchingRates: boolean | null;
|
||||
hasNotifiedRatesFailure: boolean;
|
||||
secondsRemaining: number | null;
|
||||
outputTx: string | null;
|
||||
isPostingOrder: boolean;
|
||||
|
@ -50,6 +51,7 @@ export const INITIAL_STATE: State = {
|
|||
bityOrder: {},
|
||||
shapeshiftOrder: {},
|
||||
isFetchingRates: null,
|
||||
hasNotifiedRatesFailure: false,
|
||||
secondsRemaining: null,
|
||||
outputTx: null,
|
||||
isPostingOrder: false,
|
||||
|
@ -209,14 +211,17 @@ export function swap(state: State = INITIAL_STATE, action: actionTypes.SwapActio
|
|||
};
|
||||
|
||||
case TypeKeys.SWAP_LOAD_BITY_RATES_REQUESTED:
|
||||
return {
|
||||
...state,
|
||||
isFetchingRates: true
|
||||
};
|
||||
case TypeKeys.SWAP_LOAD_SHAPESHIFT_RATES_REQUESTED:
|
||||
return {
|
||||
...state,
|
||||
isFetchingRates: true
|
||||
isFetchingRates: true,
|
||||
hasNotifiedRatesFailure: false
|
||||
};
|
||||
case TypeKeys.SWAP_LOAD_BITY_RATES_FAILED:
|
||||
case TypeKeys.SWAP_LOAD_SHAPESHIFT_RATES_FAILED:
|
||||
return {
|
||||
...state,
|
||||
hasNotifiedRatesFailure: true
|
||||
};
|
||||
case TypeKeys.SWAP_STOP_LOAD_BITY_RATES:
|
||||
return {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { Option } from 'actions/swap/actionTypes';
|
||||
import { Option, ProviderName } from 'actions/swap/actionTypes';
|
||||
import { WhitelistedCoins } from 'config';
|
||||
|
||||
export type ProviderName = ProviderName;
|
||||
|
||||
export interface SwapInput {
|
||||
id: WhitelistedCoins;
|
||||
amount: number | string;
|
||||
|
|
|
@ -2,16 +2,9 @@ import configSaga from './config';
|
|||
import deterministicWallets from './deterministicWallets';
|
||||
import notifications from './notifications';
|
||||
import rates from './rates';
|
||||
import {
|
||||
swapTimerSaga,
|
||||
pollBityOrderStatusSaga,
|
||||
postBityOrderSaga,
|
||||
postShapeshiftOrderSaga,
|
||||
pollShapeshiftOrderStatusSaga,
|
||||
restartSwapSaga
|
||||
} from './swap/orders';
|
||||
import { liteSend } from './swap/liteSend';
|
||||
import { getBityRatesSaga, getShapeShiftRatesSaga, swapProviderSaga } from './swap/rates';
|
||||
import swapOrders from './swap/orders';
|
||||
import swapLiteSend from './swap/liteSend';
|
||||
import swapRates from './swap/rates';
|
||||
import wallet from './wallet';
|
||||
import { ens } from './ens';
|
||||
import { transaction } from './transaction';
|
||||
|
@ -20,21 +13,14 @@ import gas from './gas';
|
|||
|
||||
export default {
|
||||
ens,
|
||||
liteSend,
|
||||
configSaga,
|
||||
postBityOrderSaga,
|
||||
postShapeshiftOrderSaga,
|
||||
pollBityOrderStatusSaga,
|
||||
pollShapeshiftOrderStatusSaga,
|
||||
getBityRatesSaga,
|
||||
getShapeShiftRatesSaga,
|
||||
swapTimerSaga,
|
||||
restartSwapSaga,
|
||||
swapOrders,
|
||||
swapRates,
|
||||
swapLiteSend,
|
||||
notifications,
|
||||
wallet,
|
||||
transaction,
|
||||
deterministicWallets,
|
||||
swapProviderSaga,
|
||||
rates,
|
||||
transactions,
|
||||
gas
|
||||
|
|
|
@ -108,6 +108,6 @@ export function* fetchPaymentAddress(): SagaIterator {
|
|||
return false;
|
||||
}
|
||||
|
||||
export function* liteSend(): SagaIterator {
|
||||
export default function* swapLiteSend(): SagaIterator {
|
||||
yield takeEvery(SwapTK.SWAP_CONFIGURE_LITE_SEND, handleConfigureLiteSend);
|
||||
}
|
||||
|
|
|
@ -198,14 +198,6 @@ export function* postShapeshiftOrderCreate(
|
|||
}
|
||||
}
|
||||
|
||||
export function* postBityOrderSaga(): SagaIterator {
|
||||
yield takeEvery(TypeKeys.SWAP_BITY_ORDER_CREATE_REQUESTED, postBityOrderCreate);
|
||||
}
|
||||
|
||||
export function* postShapeshiftOrderSaga(): SagaIterator {
|
||||
yield takeEvery(TypeKeys.SWAP_SHAPESHIFT_ORDER_CREATE_REQUESTED, postShapeshiftOrderCreate);
|
||||
}
|
||||
|
||||
export function* restartSwap() {
|
||||
yield put(reset());
|
||||
yield put(resetWallet());
|
||||
|
@ -214,10 +206,6 @@ export function* restartSwap() {
|
|||
yield put(loadShapeshiftRatesRequestedSwap());
|
||||
}
|
||||
|
||||
export function* restartSwapSaga(): SagaIterator {
|
||||
yield takeEvery(TypeKeys.SWAP_RESTART, restartSwap);
|
||||
}
|
||||
|
||||
export function* bityOrderTimeRemaining(): SagaIterator {
|
||||
while (true) {
|
||||
let hasShownNotification = false;
|
||||
|
@ -364,6 +352,12 @@ export function* handleOrderTimeRemaining(): SagaIterator {
|
|||
yield cancel(orderTimeRemainingTask);
|
||||
}
|
||||
|
||||
export function* swapTimerSaga(): SagaIterator {
|
||||
export default function* swapOrders(): SagaIterator {
|
||||
yield fork(handleOrderTimeRemaining);
|
||||
yield fork(pollShapeshiftOrderStatusSaga);
|
||||
yield fork(pollBityOrderStatusSaga);
|
||||
yield takeEvery(TypeKeys.SWAP_BITY_ORDER_CREATE_REQUESTED, postBityOrderCreate);
|
||||
yield takeEvery(TypeKeys.SWAP_SHAPESHIFT_ORDER_CREATE_REQUESTED, postShapeshiftOrderCreate);
|
||||
yield takeEvery(TypeKeys.SWAP_ORDER_START_TIMER, handleOrderTimeRemaining);
|
||||
yield takeEvery(TypeKeys.SWAP_RESTART, restartSwap);
|
||||
}
|
||||
|
|
|
@ -2,18 +2,21 @@ import { showNotification } from 'actions/notifications';
|
|||
import {
|
||||
loadBityRatesSucceededSwap,
|
||||
loadShapeshiftRatesSucceededSwap,
|
||||
loadBityRatesFailedSwap,
|
||||
loadShapeshiftRatesFailedSwap,
|
||||
changeSwapProvider,
|
||||
ChangeProviderSwapAcion
|
||||
} from 'actions/swap';
|
||||
import { TypeKeys } from 'actions/swap/constants';
|
||||
import { getAllRates } from 'api/bity';
|
||||
import { delay, SagaIterator } from 'redux-saga';
|
||||
import { call, select, cancel, fork, put, take, takeLatest, race } from 'redux-saga/effects';
|
||||
import { call, select, put, takeLatest, race, take, cancel, fork } from 'redux-saga/effects';
|
||||
import shapeshift from 'api/shapeshift';
|
||||
import { getSwap } from 'sagas/swap/orders';
|
||||
import { getHasNotifiedRatesFailure } from 'selectors/swap';
|
||||
|
||||
const POLLING_CYCLE = 30000;
|
||||
export const SHAPESHIFT_TIMEOUT = 10000;
|
||||
export const POLLING_CYCLE = 30000;
|
||||
|
||||
export function* loadBityRates(): SagaIterator {
|
||||
while (true) {
|
||||
|
@ -21,12 +24,23 @@ export function* loadBityRates(): SagaIterator {
|
|||
const data = yield call(getAllRates);
|
||||
yield put(loadBityRatesSucceededSwap(data));
|
||||
} catch (error) {
|
||||
yield put(showNotification('danger', error.message));
|
||||
const hasNotified = yield select(getHasNotifiedRatesFailure);
|
||||
if (!hasNotified) {
|
||||
console.error('Failed to load rates from Bity:', error);
|
||||
yield put(showNotification('danger', error.message));
|
||||
}
|
||||
yield put(loadBityRatesFailedSwap());
|
||||
}
|
||||
yield call(delay, POLLING_CYCLE);
|
||||
}
|
||||
}
|
||||
|
||||
export function* handleBityRates(): SagaIterator {
|
||||
const loadBityRatesTask = yield fork(loadBityRates);
|
||||
yield take(TypeKeys.SWAP_STOP_LOAD_BITY_RATES);
|
||||
yield cancel(loadBityRatesTask);
|
||||
}
|
||||
|
||||
export function* loadShapeshiftRates(): SagaIterator {
|
||||
while (true) {
|
||||
try {
|
||||
|
@ -40,17 +54,31 @@ export function* loadShapeshiftRates(): SagaIterator {
|
|||
if (tokens) {
|
||||
yield put(loadShapeshiftRatesSucceededSwap(tokens));
|
||||
} else {
|
||||
yield put(
|
||||
showNotification('danger', 'Error loading ShapeShift tokens - reverting to Bity')
|
||||
);
|
||||
throw new Error('ShapeShift rates request timed out.');
|
||||
}
|
||||
} catch (error) {
|
||||
yield put(showNotification('danger', `Error loading ShapeShift tokens - ${error}`));
|
||||
const hasNotified = yield select(getHasNotifiedRatesFailure);
|
||||
if (!hasNotified) {
|
||||
console.error('Failed to fetch rates from shapeshift:', error);
|
||||
yield put(
|
||||
showNotification(
|
||||
'danger',
|
||||
'Failed to load swap rates from ShapeShift, please try again later'
|
||||
)
|
||||
);
|
||||
}
|
||||
yield put(loadShapeshiftRatesFailedSwap());
|
||||
}
|
||||
yield call(delay, POLLING_CYCLE);
|
||||
}
|
||||
}
|
||||
|
||||
export function* handleShapeshiftRates(): SagaIterator {
|
||||
const loadShapeshiftRatesTask = yield fork(loadShapeshiftRates);
|
||||
yield take(TypeKeys.SWAP_STOP_LOAD_SHAPESHIFT_RATES);
|
||||
yield cancel(loadShapeshiftRatesTask);
|
||||
}
|
||||
|
||||
export function* swapProvider(action: ChangeProviderSwapAcion): SagaIterator {
|
||||
const swap = yield select(getSwap);
|
||||
if (swap.provider !== action.payload) {
|
||||
|
@ -58,31 +86,8 @@ export function* swapProvider(action: ChangeProviderSwapAcion): SagaIterator {
|
|||
}
|
||||
}
|
||||
|
||||
// Fork our recurring API call, watch for the need to cancel.
|
||||
export function* handleBityRates(): SagaIterator {
|
||||
const loadBityRatesTask = yield fork(loadBityRates);
|
||||
yield take(TypeKeys.SWAP_STOP_LOAD_BITY_RATES);
|
||||
yield cancel(loadBityRatesTask);
|
||||
}
|
||||
|
||||
// Watch for latest SWAP_LOAD_BITY_RATES_REQUESTED action.
|
||||
export function* getBityRatesSaga(): SagaIterator {
|
||||
export default function* swapRates(): SagaIterator {
|
||||
yield takeLatest(TypeKeys.SWAP_LOAD_BITY_RATES_REQUESTED, handleBityRates);
|
||||
}
|
||||
|
||||
// Fork our API call
|
||||
export function* handleShapeShiftRates(): SagaIterator {
|
||||
const loadShapeShiftRatesTask = yield fork(loadShapeshiftRates);
|
||||
yield take(TypeKeys.SWAP_STOP_LOAD_SHAPESHIFT_RATES);
|
||||
yield cancel(loadShapeShiftRatesTask);
|
||||
}
|
||||
|
||||
// Watch for SWAP_LOAD_SHAPESHIFT_RATES_REQUESTED action.
|
||||
export function* getShapeShiftRatesSaga(): SagaIterator {
|
||||
yield takeLatest(TypeKeys.SWAP_LOAD_SHAPESHIFT_RATES_REQUESTED, handleShapeShiftRates);
|
||||
}
|
||||
|
||||
// Watch for provider swaps
|
||||
export function* swapProviderSaga(): SagaIterator {
|
||||
yield takeLatest(TypeKeys.SWAP_LOAD_SHAPESHIFT_RATES_REQUESTED, handleShapeshiftRates);
|
||||
yield takeLatest(TypeKeys.SWAP_CHANGE_PROVIDER, swapProvider);
|
||||
}
|
||||
|
|
|
@ -4,3 +4,5 @@ const getSwap = (state: AppState) => state.swap;
|
|||
export const getOrigin = (state: AppState) => getSwap(state).origin;
|
||||
export const getPaymentAddress = (state: AppState) => getSwap(state).paymentAddress;
|
||||
export const shouldDisplayLiteSend = (state: AppState) => getSwap(state).showLiteSend;
|
||||
export const getHasNotifiedRatesFailure = (state: AppState) =>
|
||||
getSwap(state).hasNotifiedRatesFailure;
|
||||
|
|
|
@ -45,8 +45,6 @@ exports[`render snapshot 1`] = `
|
|||
isFetchingRates={null}
|
||||
isOffline={false}
|
||||
isPostingOrder={false}
|
||||
loadBityRatesRequestedSwap={[Function]}
|
||||
loadShapeshiftRatesRequestedSwap={[Function]}
|
||||
location={
|
||||
Object {
|
||||
"hash": "",
|
||||
|
|
|
@ -30,26 +30,14 @@ import { getOrderStatus, postOrder } from 'api/bity';
|
|||
import shapeshift from 'api/shapeshift';
|
||||
import { State as SwapState, INITIAL_STATE as INITIAL_SWAP_STATE } from 'reducers/swap';
|
||||
import { delay } from 'redux-saga';
|
||||
import {
|
||||
call,
|
||||
cancel,
|
||||
apply,
|
||||
cancelled,
|
||||
fork,
|
||||
put,
|
||||
select,
|
||||
take,
|
||||
takeEvery
|
||||
} from 'redux-saga/effects';
|
||||
import { call, cancel, apply, cancelled, fork, put, select, take } from 'redux-saga/effects';
|
||||
import {
|
||||
getSwap,
|
||||
pollBityOrderStatus,
|
||||
pollBityOrderStatusSaga,
|
||||
postBityOrderCreate,
|
||||
postBityOrderSaga,
|
||||
pollShapeshiftOrderStatus,
|
||||
pollShapeshiftOrderStatusSaga,
|
||||
postShapeshiftOrderSaga,
|
||||
shapeshiftOrderTimeRemaining,
|
||||
bityOrderTimeRemaining,
|
||||
ORDER_TIMEOUT_MESSAGE,
|
||||
|
@ -455,26 +443,6 @@ describe('postShapeshiftOrderCreate*', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('postBityOrderSaga*', () => {
|
||||
const gen = postBityOrderSaga();
|
||||
|
||||
it('should takeEvery SWAP_BITY_ORDER_CREATE_REQUESTED', () => {
|
||||
expect(gen.next().value).toEqual(
|
||||
takeEvery(TypeKeys.SWAP_BITY_ORDER_CREATE_REQUESTED, postBityOrderCreate)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('postShapeshiftOrderSaga*', () => {
|
||||
const gen = postShapeshiftOrderSaga();
|
||||
|
||||
it('should takeEvery SWAP_SHAPESHIFT_ORDER_CREATE_REQUESTED', () => {
|
||||
expect(gen.next().value).toEqual(
|
||||
takeEvery(TypeKeys.SWAP_SHAPESHIFT_ORDER_CREATE_REQUESTED, postShapeshiftOrderCreate)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('bityOrderTimeRemaining*', () => {
|
||||
const orderTime = new Date().toISOString();
|
||||
const orderTimeExpired = new Date().getTime() - ELEVEN_SECONDS;
|
||||
|
|
|
@ -1,24 +1,28 @@
|
|||
import { showNotification } from 'actions/notifications';
|
||||
import { loadBityRatesSucceededSwap, loadShapeshiftRatesSucceededSwap } from 'actions/swap';
|
||||
import {
|
||||
loadBityRatesSucceededSwap,
|
||||
loadShapeshiftRatesSucceededSwap,
|
||||
loadShapeshiftRatesFailedSwap,
|
||||
loadBityRatesFailedSwap
|
||||
} from 'actions/swap';
|
||||
import { getAllRates } from 'api/bity';
|
||||
import { delay } from 'redux-saga';
|
||||
import { call, cancel, fork, put, race, take, takeLatest } from 'redux-saga/effects';
|
||||
import { call, cancel, fork, put, race, take, select } from 'redux-saga/effects';
|
||||
import { createMockTask } from 'redux-saga/utils';
|
||||
import {
|
||||
loadBityRates,
|
||||
handleBityRates,
|
||||
getBityRatesSaga,
|
||||
loadShapeshiftRates,
|
||||
getShapeShiftRatesSaga,
|
||||
handleBityRates,
|
||||
handleShapeshiftRates,
|
||||
SHAPESHIFT_TIMEOUT,
|
||||
handleShapeShiftRates
|
||||
POLLING_CYCLE
|
||||
} from 'sagas/swap/rates';
|
||||
import shapeshift from 'api/shapeshift';
|
||||
import { TypeKeys } from 'actions/swap/constants';
|
||||
import { getHasNotifiedRatesFailure } from 'selectors/swap';
|
||||
|
||||
describe('loadBityRates*', () => {
|
||||
const gen1 = loadBityRates();
|
||||
const gen2 = loadBityRates();
|
||||
const apiResponse = {
|
||||
BTCETH: {
|
||||
id: 'BTCETH',
|
||||
|
@ -31,6 +35,7 @@ describe('loadBityRates*', () => {
|
|||
rate: 0.042958
|
||||
}
|
||||
};
|
||||
const err = { message: 'error' };
|
||||
let random;
|
||||
|
||||
beforeAll(() => {
|
||||
|
@ -50,20 +55,30 @@ describe('loadBityRates*', () => {
|
|||
expect(gen1.next(apiResponse).value).toEqual(put(loadBityRatesSucceededSwap(apiResponse)));
|
||||
});
|
||||
|
||||
it('should call delay for 5 seconds', () => {
|
||||
expect(gen1.next().value).toEqual(call(delay, 30000));
|
||||
it(`should delay for ${POLLING_CYCLE}ms`, () => {
|
||||
expect(gen1.next().value).toEqual(call(delay, POLLING_CYCLE));
|
||||
});
|
||||
|
||||
it('should handle an exception', () => {
|
||||
const err = { message: 'error' };
|
||||
gen2.next();
|
||||
expect((gen2 as any).throw(err).value).toEqual(put(showNotification('danger', err.message)));
|
||||
const errGen = loadBityRates();
|
||||
errGen.next();
|
||||
expect((errGen as any).throw(err).value).toEqual(select(getHasNotifiedRatesFailure));
|
||||
expect(errGen.next(false).value).toEqual(put(showNotification('danger', err.message)));
|
||||
expect(errGen.next().value).toEqual(put(loadBityRatesFailedSwap()));
|
||||
expect(errGen.next().value).toEqual(call(delay, POLLING_CYCLE));
|
||||
});
|
||||
|
||||
it('should not notify on subsequent exceptions', () => {
|
||||
const noNotifyErrGen = loadBityRates();
|
||||
noNotifyErrGen.next();
|
||||
expect((noNotifyErrGen as any).throw(err).value).toEqual(select(getHasNotifiedRatesFailure));
|
||||
expect(noNotifyErrGen.next(true).value).toEqual(put(loadBityRatesFailedSwap()));
|
||||
expect(noNotifyErrGen.next().value).toEqual(call(delay, POLLING_CYCLE));
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadShapeshiftRates*', () => {
|
||||
const gen1 = loadShapeshiftRates();
|
||||
const gen2 = loadShapeshiftRates();
|
||||
|
||||
const apiResponse = {
|
||||
['1SSTANT']: {
|
||||
|
@ -87,6 +102,7 @@ describe('loadShapeshiftRates*', () => {
|
|||
min: 7.86382979
|
||||
}
|
||||
};
|
||||
const err = 'error';
|
||||
let random;
|
||||
|
||||
beforeAll(() => {
|
||||
|
@ -113,16 +129,30 @@ describe('loadShapeshiftRates*', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('should call delay for 30 seconds', () => {
|
||||
expect(gen1.next().value).toEqual(call(delay, 30000));
|
||||
it(`should delay for ${POLLING_CYCLE}ms`, () => {
|
||||
expect(gen1.next().value).toEqual(call(delay, POLLING_CYCLE));
|
||||
});
|
||||
|
||||
it('should handle an exception', () => {
|
||||
const err = 'error';
|
||||
gen2.next();
|
||||
expect((gen2 as any).throw(err).value).toEqual(
|
||||
put(showNotification('danger', `Error loading ShapeShift tokens - ${err}`))
|
||||
const errGen = loadShapeshiftRates();
|
||||
errGen.next();
|
||||
expect((errGen as any).throw(err).value).toEqual(select(getHasNotifiedRatesFailure));
|
||||
expect(errGen.next(false).value).toEqual(
|
||||
put(
|
||||
showNotification(
|
||||
'danger',
|
||||
'Failed to load swap rates from ShapeShift, please try again later'
|
||||
)
|
||||
)
|
||||
);
|
||||
expect(errGen.next().value).toEqual(put(loadShapeshiftRatesFailedSwap()));
|
||||
});
|
||||
|
||||
it('should not notify on subsequent exceptions', () => {
|
||||
const noNotifyErrGen = loadShapeshiftRates();
|
||||
noNotifyErrGen.next();
|
||||
expect((noNotifyErrGen as any).throw(err).value).toEqual(select(getHasNotifiedRatesFailure));
|
||||
expect(noNotifyErrGen.next(true).value).toEqual(put(loadShapeshiftRatesFailedSwap()));
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -148,7 +178,7 @@ describe('handleBityRates*', () => {
|
|||
});
|
||||
|
||||
describe('handleShapeshiftRates*', () => {
|
||||
const gen = handleShapeShiftRates();
|
||||
const gen = handleShapeshiftRates();
|
||||
const mockTask = createMockTask();
|
||||
|
||||
it('should fork loadShapeshiftRates', () => {
|
||||
|
@ -167,23 +197,3 @@ describe('handleShapeshiftRates*', () => {
|
|||
expect(gen.next().done).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBityRatesSaga*', () => {
|
||||
const gen = getBityRatesSaga();
|
||||
|
||||
it('should takeLatest SWAP_LOAD_BITY_RATES_REQUESTED', () => {
|
||||
expect(gen.next().value).toEqual(
|
||||
takeLatest(TypeKeys.SWAP_LOAD_BITY_RATES_REQUESTED, handleBityRates)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getShapeshiftRatesSaga*', () => {
|
||||
const gen = getShapeShiftRatesSaga();
|
||||
|
||||
it('should takeLatest SWAP_LOAD_BITY_RATES_REQUESTED', () => {
|
||||
expect(gen.next().value).toEqual(
|
||||
takeLatest(TypeKeys.SWAP_LOAD_SHAPESHIFT_RATES_REQUESTED, handleShapeShiftRates)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue