2021-03-25 12:24:49 +01:00
|
|
|
// @flow
|
|
|
|
import * as ACTIONS from 'constants/action_types';
|
|
|
|
import { handleActions } from 'util/redux-utils';
|
|
|
|
|
2021-04-11 07:21:19 +02:00
|
|
|
const SWAP_HISTORY_LENGTH_LIMIT = 10;
|
|
|
|
|
|
|
|
function getBottomEntries(array, count) {
|
|
|
|
const curCount = array.length;
|
|
|
|
if (curCount < count) {
|
|
|
|
return array;
|
|
|
|
} else {
|
|
|
|
return array.slice(curCount - count);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-25 12:24:49 +01:00
|
|
|
const defaultState: CoinSwapState = {
|
2021-04-04 09:10:55 +02:00
|
|
|
coinSwaps: [],
|
2021-03-25 12:24:49 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
export default handleActions(
|
|
|
|
{
|
2021-04-08 11:02:37 +02:00
|
|
|
[ACTIONS.ADD_COIN_SWAP]: (state: CoinSwapState, action: CoinSwapAddAction): CoinSwapState => {
|
2021-04-04 09:10:55 +02:00
|
|
|
const { coinSwaps } = state;
|
2021-04-08 11:02:37 +02:00
|
|
|
const { chargeCode } = action.data;
|
2021-04-04 09:10:55 +02:00
|
|
|
|
2021-04-08 11:02:37 +02:00
|
|
|
const newCoinSwaps = coinSwaps.slice();
|
|
|
|
if (!newCoinSwaps.find((x) => x.chargeCode === chargeCode)) {
|
|
|
|
newCoinSwaps.push({ ...action.data });
|
2021-03-25 12:24:49 +01:00
|
|
|
}
|
2021-04-04 09:10:55 +02:00
|
|
|
|
2021-03-25 12:24:49 +01:00
|
|
|
return {
|
2021-04-08 11:02:37 +02:00
|
|
|
...state,
|
2021-04-04 09:10:55 +02:00
|
|
|
coinSwaps: newCoinSwaps,
|
2021-03-25 12:24:49 +01:00
|
|
|
};
|
|
|
|
},
|
2021-04-04 09:10:55 +02:00
|
|
|
[ACTIONS.REMOVE_COIN_SWAP]: (state: CoinSwapState, action: CoinSwapRemoveAction): CoinSwapState => {
|
|
|
|
const { coinSwaps } = state;
|
2021-04-08 11:02:37 +02:00
|
|
|
const { chargeCode } = action.data;
|
2021-04-04 09:10:55 +02:00
|
|
|
let newCoinSwaps = coinSwaps.slice();
|
2021-04-08 11:02:37 +02:00
|
|
|
newCoinSwaps = newCoinSwaps.filter((x) => x.chargeCode !== chargeCode);
|
|
|
|
return {
|
|
|
|
...state,
|
|
|
|
coinSwaps: newCoinSwaps,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
[ACTIONS.COIN_SWAP_STATUS_RECEIVED]: (state: CoinSwapState, action: any) => {
|
|
|
|
const { coinSwaps } = state;
|
|
|
|
const newCoinSwaps = coinSwaps.slice();
|
|
|
|
|
|
|
|
let exchange;
|
|
|
|
let charge;
|
|
|
|
|
|
|
|
if (action.data.event_data) {
|
|
|
|
// Source: Websocket
|
|
|
|
exchange = { lbc_txid: action.data.lbc_txid };
|
|
|
|
charge = action.data.event_data;
|
|
|
|
} else {
|
|
|
|
// Source: btc/status
|
|
|
|
exchange = action.data.Exchange;
|
|
|
|
charge = action.data.Charge.data;
|
|
|
|
}
|
|
|
|
|
|
|
|
const calculateLbcAmount = (pricing, exchange, fallback) => {
|
|
|
|
if (!exchange || !exchange.rate) {
|
|
|
|
return fallback || 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const btcAmount = pricing['bitcoin'].amount;
|
|
|
|
const SATOSHIS = 100000000;
|
|
|
|
return (btcAmount * SATOSHIS) / exchange.rate;
|
|
|
|
};
|
|
|
|
|
|
|
|
const timeline = charge.timeline;
|
|
|
|
const lastTimeline = timeline[timeline.length - 1];
|
|
|
|
|
|
|
|
const index = newCoinSwaps.findIndex((x) => x.chargeCode === charge.code);
|
|
|
|
if (index > -1) {
|
|
|
|
newCoinSwaps[index] = {
|
|
|
|
chargeCode: charge.code,
|
|
|
|
coins: Object.keys(charge.addresses),
|
|
|
|
sendAddresses: charge.addresses,
|
|
|
|
sendAmounts: charge.pricing,
|
|
|
|
lbcAmount: calculateLbcAmount(charge.pricing, exchange, newCoinSwaps[index].lbcAmount),
|
|
|
|
status: {
|
|
|
|
status: lastTimeline.status,
|
|
|
|
receiptCurrency: lastTimeline.payment.value.currency,
|
|
|
|
receiptTxid: lastTimeline.payment.transaction_id,
|
|
|
|
lbcTxid: exchange.lbc_txid || '',
|
|
|
|
},
|
|
|
|
};
|
|
|
|
} else {
|
2021-04-11 07:21:19 +02:00
|
|
|
// If a pending swap is removed, the websocket will return an update
|
|
|
|
// when it expires, for example, causing the entry to re-appear. This
|
|
|
|
// might be a good thing (e.g. to get back accidental removals), but it
|
|
|
|
// actually causes synchronization confusion across multiple instances.
|
|
|
|
const IGNORED_DELETED_SWAPS = true;
|
|
|
|
|
|
|
|
if (!IGNORED_DELETED_SWAPS) {
|
|
|
|
newCoinSwaps.push({
|
|
|
|
chargeCode: charge.code,
|
|
|
|
coins: Object.keys(charge.addresses),
|
|
|
|
sendAddresses: charge.addresses,
|
|
|
|
sendAmounts: charge.pricing,
|
|
|
|
lbcAmount: calculateLbcAmount(charge.pricing, exchange, 0),
|
|
|
|
status: {
|
|
|
|
status: lastTimeline.status,
|
|
|
|
receiptCurrency: lastTimeline.payment.value.currency,
|
|
|
|
receiptTxid: lastTimeline.payment.transaction_id,
|
|
|
|
lbcTxid: exchange.lbc_txid || '',
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
2021-04-08 11:02:37 +02:00
|
|
|
}
|
|
|
|
|
2021-03-25 12:24:49 +01:00
|
|
|
return {
|
2021-04-08 11:02:37 +02:00
|
|
|
...state,
|
2021-04-04 09:10:55 +02:00
|
|
|
coinSwaps: newCoinSwaps,
|
2021-03-25 12:24:49 +01:00
|
|
|
};
|
|
|
|
},
|
2022-01-06 21:30:24 +01:00
|
|
|
[ACTIONS.SYNC_STATE_POPULATE]: (state: CoinSwapState, action: { data: { coinSwapCodes: ?Array<string> } }) => {
|
2021-04-08 11:02:37 +02:00
|
|
|
const { coinSwapCodes } = action.data;
|
2021-04-11 07:21:19 +02:00
|
|
|
const newCoinSwaps = [];
|
2021-04-08 11:02:37 +02:00
|
|
|
|
|
|
|
if (coinSwapCodes) {
|
|
|
|
coinSwapCodes.forEach((chargeCode) => {
|
2021-04-11 07:21:19 +02:00
|
|
|
if (chargeCode && typeof chargeCode === 'string') {
|
|
|
|
const existingSwap = state.coinSwaps.find((x) => x.chargeCode === chargeCode);
|
|
|
|
if (existingSwap) {
|
|
|
|
newCoinSwaps.push({ ...existingSwap });
|
|
|
|
} else {
|
|
|
|
newCoinSwaps.push({
|
|
|
|
// Just restore the 'chargeCode', and query the other data
|
|
|
|
// via 'btc/status' later.
|
|
|
|
chargeCode: chargeCode,
|
|
|
|
coins: [],
|
|
|
|
sendAddresses: {},
|
|
|
|
sendAmounts: {},
|
|
|
|
lbcAmount: 0,
|
|
|
|
});
|
|
|
|
}
|
2021-04-08 11:02:37 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-03-25 12:24:49 +01:00
|
|
|
return {
|
|
|
|
...state,
|
2021-04-11 07:21:19 +02:00
|
|
|
coinSwaps: getBottomEntries(newCoinSwaps, SWAP_HISTORY_LENGTH_LIMIT),
|
2021-03-25 12:24:49 +01:00
|
|
|
};
|
|
|
|
},
|
|
|
|
},
|
|
|
|
defaultState
|
|
|
|
);
|