Save entire swap info instead of just the address.
- Users should be able to see the entered and promised amount, otherwise they might forget how much to send over. - This change also prepares for the future upgrade to support multiple coins.
This commit is contained in:
parent
9c808e2b5e
commit
b2630f6f21
10 changed files with 97 additions and 77 deletions
21
flow-typed/CoinSwap.js
vendored
21
flow-typed/CoinSwap.js
vendored
|
@ -1,10 +1,27 @@
|
|||
declare type CoinSwapInfo = {
|
||||
coin: string,
|
||||
sendAddress: string,
|
||||
sendAmount: number,
|
||||
lbcAmount: number,
|
||||
}
|
||||
|
||||
declare type CoinSwapState = {
|
||||
btcAddresses: Array<string>
|
||||
coinSwaps: Array<CoinSwapInfo>
|
||||
};
|
||||
|
||||
declare type CoinSwapAction = {
|
||||
type: string,
|
||||
data: {
|
||||
btcAddress: string,
|
||||
coin: string,
|
||||
sendAddress: string,
|
||||
sendAmount: number,
|
||||
lbcAmount: number,
|
||||
},
|
||||
};
|
||||
|
||||
declare type CoinSwapRemoveAction = {
|
||||
type: string,
|
||||
data: {
|
||||
sendAddress: string,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -2,20 +2,20 @@ import { connect } from 'react-redux';
|
|||
import { withRouter } from 'react-router';
|
||||
import WalletSwap from './view';
|
||||
import { doOpenModal } from 'redux/actions/app';
|
||||
import { doAddBtcAddress } from 'redux/actions/coinSwap';
|
||||
import { doAddCoinSwap } from 'redux/actions/coinSwap';
|
||||
import { doToast } from 'redux/actions/notifications';
|
||||
import { selectBtcAddresses } from 'redux/selectors/coinSwap';
|
||||
import { selectCoinSwaps } from 'redux/selectors/coinSwap';
|
||||
import { selectReceiveAddress, doGetNewAddress, doCheckAddressIsMine } from 'lbry-redux';
|
||||
|
||||
const select = (state, props) => ({
|
||||
receiveAddress: selectReceiveAddress(state),
|
||||
btcAddresses: selectBtcAddresses(state),
|
||||
coinSwaps: selectCoinSwaps(state),
|
||||
});
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
openModal: (modal, props) => dispatch(doOpenModal(modal, props)),
|
||||
doToast: (options) => dispatch(doToast(options)),
|
||||
doAddBtcAddress: (address) => dispatch(doAddBtcAddress(address)),
|
||||
addCoinSwap: (coinSwap) => dispatch(doAddCoinSwap(coinSwap)),
|
||||
getNewAddress: () => dispatch(doGetNewAddress()),
|
||||
checkAddressIsMine: (address) => dispatch(doCheckAddressIsMine(address)),
|
||||
});
|
||||
|
|
|
@ -47,24 +47,16 @@ const NAG_RATE_CALL_FAILED = 'Unable to obtain exchange rate. Try again later.';
|
|||
|
||||
type Props = {
|
||||
receiveAddress: string,
|
||||
btcAddresses: Array<string>,
|
||||
coinSwaps: Array<CoinSwapInfo>,
|
||||
doToast: ({ message: string }) => void,
|
||||
doAddBtcAddress: (string) => void,
|
||||
addCoinSwap: (CoinSwapInfo) => void,
|
||||
getNewAddress: () => void,
|
||||
checkAddressIsMine: (string) => void,
|
||||
openModal: (string, {}) => void,
|
||||
};
|
||||
|
||||
function WalletSwap(props: Props) {
|
||||
const {
|
||||
receiveAddress,
|
||||
doToast,
|
||||
btcAddresses,
|
||||
doAddBtcAddress,
|
||||
getNewAddress,
|
||||
checkAddressIsMine,
|
||||
openModal,
|
||||
} = props;
|
||||
const { receiveAddress, doToast, coinSwaps, addCoinSwap, getNewAddress, checkAddressIsMine, openModal } = props;
|
||||
|
||||
const [btc, setBtc] = usePersistedState('swap-btc-amount', 0.001);
|
||||
const [btcError, setBtcError] = React.useState();
|
||||
|
@ -92,9 +84,9 @@ function WalletSwap(props: Props) {
|
|||
setBtcAddress(null);
|
||||
}
|
||||
|
||||
function removeBtcAddress(btcAddress) {
|
||||
function removeCoinSwap(sendAddress) {
|
||||
openModal(MODALS.CONFIRM_REMOVE_BTC_SWAP_ADDRESS, {
|
||||
btcAddress: btcAddress,
|
||||
sendAddress: sendAddress,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -236,7 +228,12 @@ function WalletSwap(props: Props) {
|
|||
})
|
||||
.then((result) => {
|
||||
setBtcAddress(result);
|
||||
doAddBtcAddress(result);
|
||||
addCoinSwap({
|
||||
coin: 'btc',
|
||||
sendAddress: result,
|
||||
sendAmount: btc,
|
||||
lbcAmount: lbc,
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
setNag({ msg: err === INTERNAL_APIS_DOWN ? NAG_SWAP_CALL_FAILED : err.message, type: 'error' });
|
||||
|
@ -259,8 +256,8 @@ function WalletSwap(props: Props) {
|
|||
setNag(null);
|
||||
setIsRefreshingStatus(true);
|
||||
|
||||
btcAddresses.forEach((x) => {
|
||||
queryStatus(x, null, null);
|
||||
coinSwaps.forEach((x) => {
|
||||
queryStatus(x.sendAddress, null, null);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -357,9 +354,7 @@ function WalletSwap(props: Props) {
|
|||
disabled={isSwapping || isNaN(btc) || btc === 0 || lbc === 0 || btcError}
|
||||
label={isSwapping ? __('Processing...') : __('Start Swap')}
|
||||
/>
|
||||
{btcAddresses.length !== 0 && (
|
||||
<Button button="link" label={__('View Past Swaps')} onClick={handleViewPastSwaps} />
|
||||
)}
|
||||
{coinSwaps.length !== 0 && <Button button="link" label={__('View Past Swaps')} onClick={handleViewPastSwaps} />}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
@ -424,37 +419,36 @@ function WalletSwap(props: Props) {
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{btcAddresses.length === 0 && (
|
||||
{coinSwaps.length === 0 && (
|
||||
<tr>
|
||||
<td>{'---'}</td>
|
||||
</tr>
|
||||
)}
|
||||
{btcAddresses.length !== 0 &&
|
||||
btcAddresses.map((x) => {
|
||||
const shortBtcAddress = x.substring(0, 7);
|
||||
{coinSwaps.length !== 0 &&
|
||||
coinSwaps.map((x) => {
|
||||
return (
|
||||
<tr key={x}>
|
||||
<tr key={x.sendAddress}>
|
||||
<td>
|
||||
<Button
|
||||
button="link"
|
||||
className="button--hash-id"
|
||||
title={x}
|
||||
label={shortBtcAddress}
|
||||
title={x.sendAddress}
|
||||
label={x.sendAddress.substring(0, 7)}
|
||||
onClick={() => {
|
||||
clipboard.writeText(x);
|
||||
clipboard.writeText(x.sendAddress);
|
||||
doToast({
|
||||
message: __('Address copied.'),
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</td>
|
||||
<td>{isRefreshingStatus ? '...' : getStatusStr(x)}</td>
|
||||
<td>{isRefreshingStatus ? '...' : getStatusStr(x.sendAddress)}</td>
|
||||
<td>
|
||||
<Button
|
||||
button="link"
|
||||
icon={ICONS.REMOVE}
|
||||
title={__('Remove address')}
|
||||
onClick={() => removeBtcAddress(x)}
|
||||
onClick={() => removeCoinSwap(x.sendAddress)}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -467,7 +461,7 @@ function WalletSwap(props: Props) {
|
|||
</div>
|
||||
<div className="section__actions">
|
||||
<Button autoFocus onClick={handleCancelPending} button="primary" label={__('Go Back')} />
|
||||
{btcAddresses.length !== 0 && !isRefreshingStatus && (
|
||||
{coinSwaps.length !== 0 && !isRefreshingStatus && (
|
||||
<Button button="link" label={__('Refresh')} onClick={handleViewPastSwaps} />
|
||||
)}
|
||||
{isRefreshingStatus && <Spinner type="small" />}
|
||||
|
|
|
@ -283,8 +283,8 @@ export const COMMENT_RECEIVED = 'COMMENT_RECEIVED';
|
|||
export const TOGGLE_BLOCK_CHANNEL = 'TOGGLE_BLOCK_CHANNEL';
|
||||
|
||||
// Coin swap
|
||||
export const ADD_BTC_ADDRESS = 'ADD_BTC_ADDRESS';
|
||||
export const REMOVE_BTC_ADDRESS = 'REMOVE_BTC_ADDRESS';
|
||||
export const ADD_COIN_SWAP = 'ADD_COIN_SWAP';
|
||||
export const REMOVE_COIN_SWAP = 'REMOVE_COIN_SWAP';
|
||||
|
||||
// Tags
|
||||
export const TOGGLE_TAG_FOLLOW = 'TOGGLE_TAG_FOLLOW';
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { doHideModal } from 'redux/actions/app';
|
||||
import ModalRemoveBtcSwapAddress from './view';
|
||||
import { doRemoveBtcAddress } from 'redux/actions/coinSwap';
|
||||
import { doRemoveCoinSwap } from 'redux/actions/coinSwap';
|
||||
|
||||
const select = (state, props) => ({});
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
doRemoveBtcAddress: (btcAddress) => dispatch(doRemoveBtcAddress(btcAddress)),
|
||||
removeCoinSwap: (sendAddress) => dispatch(doRemoveCoinSwap(sendAddress)),
|
||||
closeModal: () => dispatch(doHideModal()),
|
||||
});
|
||||
|
||||
|
|
|
@ -6,19 +6,19 @@ import Card from 'component/common/card';
|
|||
import I18nMessage from 'component/i18nMessage';
|
||||
|
||||
type Props = {
|
||||
btcAddress: string,
|
||||
doRemoveBtcAddress: (string) => void,
|
||||
sendAddress: string,
|
||||
removeCoinSwap: (string) => void,
|
||||
closeModal: () => void,
|
||||
};
|
||||
|
||||
function ModalRemoveBtcSwapAddress(props: Props) {
|
||||
const { btcAddress, doRemoveBtcAddress, closeModal } = props;
|
||||
const { sendAddress, removeCoinSwap, closeModal } = props;
|
||||
|
||||
return (
|
||||
<Modal isOpen contentLabel={__('Confirm Address Removal')} type="card" onAborted={closeModal}>
|
||||
<Card
|
||||
title={__('Remove BTC Swap Address')}
|
||||
subtitle={<I18nMessage tokens={{ btc_address: <em>{`${btcAddress}`}</em> }}>Remove %btc_address%?</I18nMessage>}
|
||||
subtitle={<I18nMessage tokens={{ address: <em>{`${sendAddress}`}</em> }}>Remove %address%?</I18nMessage>}
|
||||
body={<p className="help--warning">{__('This process cannot be reversed.')}</p>}
|
||||
actions={
|
||||
<>
|
||||
|
@ -27,7 +27,7 @@ function ModalRemoveBtcSwapAddress(props: Props) {
|
|||
button="primary"
|
||||
label={__('OK')}
|
||||
onClick={() => {
|
||||
doRemoveBtcAddress(btcAddress);
|
||||
removeCoinSwap(sendAddress);
|
||||
closeModal();
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -3,7 +3,7 @@ import * as ACTIONS from 'constants/action_types';
|
|||
import { selectPrefsReady } from 'redux/selectors/sync';
|
||||
import { doAlertWaitingForSync } from 'redux/actions/app';
|
||||
|
||||
export const doAddBtcAddress = (btcAddress: string) => (dispatch: Dispatch, getState: GetState) => {
|
||||
export const doAddCoinSwap = (coinSwap: CoinSwapInfo) => (dispatch: Dispatch, getState: GetState) => {
|
||||
const state = getState();
|
||||
const ready = selectPrefsReady(state);
|
||||
|
||||
|
@ -12,14 +12,17 @@ export const doAddBtcAddress = (btcAddress: string) => (dispatch: Dispatch, getS
|
|||
}
|
||||
|
||||
dispatch({
|
||||
type: ACTIONS.ADD_BTC_ADDRESS,
|
||||
type: ACTIONS.ADD_COIN_SWAP,
|
||||
data: {
|
||||
btcAddress,
|
||||
coin: coinSwap.coin,
|
||||
sendAddress: coinSwap.sendAddress,
|
||||
sendAmount: coinSwap.sendAmount,
|
||||
lbcAmount: coinSwap.lbcAmount,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const doRemoveBtcAddress = (btcAddress: string) => (dispatch: Dispatch, getState: GetState) => {
|
||||
export const doRemoveCoinSwap = (sendAddress: string) => (dispatch: Dispatch, getState: GetState) => {
|
||||
const state = getState();
|
||||
const ready = selectPrefsReady(state);
|
||||
|
||||
|
@ -28,9 +31,9 @@ export const doRemoveBtcAddress = (btcAddress: string) => (dispatch: Dispatch, g
|
|||
}
|
||||
|
||||
dispatch({
|
||||
type: ACTIONS.REMOVE_BTC_ADDRESS,
|
||||
type: ACTIONS.REMOVE_COIN_SWAP,
|
||||
data: {
|
||||
btcAddress,
|
||||
sendAddress,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
|
@ -4,41 +4,47 @@ import { ACTIONS as LBRY_REDUX_ACTIONS } from 'lbry-redux';
|
|||
import { handleActions } from 'util/redux-utils';
|
||||
|
||||
const defaultState: CoinSwapState = {
|
||||
btcAddresses: [],
|
||||
coinSwaps: [],
|
||||
};
|
||||
|
||||
export default handleActions(
|
||||
{
|
||||
[ACTIONS.ADD_BTC_ADDRESS]: (state: CoinSwapState, action: CoinSwapAction): CoinSwapState => {
|
||||
const { btcAddresses } = state;
|
||||
const { btcAddress } = action.data;
|
||||
let newBtcAddresses = btcAddresses.slice();
|
||||
if (!newBtcAddresses.includes(btcAddress)) {
|
||||
newBtcAddresses.push(btcAddress);
|
||||
[ACTIONS.ADD_COIN_SWAP]: (state: CoinSwapState, action: CoinSwapAction): CoinSwapState => {
|
||||
const { coinSwaps } = state;
|
||||
const { coin, sendAddress, sendAmount, lbcAmount } = action.data;
|
||||
|
||||
let newCoinSwaps = coinSwaps.slice();
|
||||
if (!newCoinSwaps.find((x) => x.sendAddress === sendAddress)) {
|
||||
newCoinSwaps.push({
|
||||
coin: coin,
|
||||
sendAddress: sendAddress,
|
||||
sendAmount: sendAmount,
|
||||
lbcAmount: lbcAmount,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
btcAddresses: newBtcAddresses,
|
||||
coinSwaps: newCoinSwaps,
|
||||
};
|
||||
},
|
||||
[ACTIONS.REMOVE_BTC_ADDRESS]: (state: CoinSwapState, action: CoinSwapAction): CoinSwapState => {
|
||||
const { btcAddresses } = state;
|
||||
const { btcAddress } = action.data;
|
||||
let newBtcAddresses = btcAddresses.slice();
|
||||
newBtcAddresses = newBtcAddresses.filter((x) => x !== btcAddress);
|
||||
[ACTIONS.REMOVE_COIN_SWAP]: (state: CoinSwapState, action: CoinSwapRemoveAction): CoinSwapState => {
|
||||
const { coinSwaps } = state;
|
||||
const { sendAddress } = action.data;
|
||||
let newCoinSwaps = coinSwaps.slice();
|
||||
newCoinSwaps = newCoinSwaps.filter((x) => x.sendAddress !== sendAddress);
|
||||
return {
|
||||
btcAddresses: newBtcAddresses,
|
||||
coinSwaps: newCoinSwaps,
|
||||
};
|
||||
},
|
||||
[LBRY_REDUX_ACTIONS.USER_STATE_POPULATE]: (
|
||||
state: CoinSwapState,
|
||||
action: { data: { btcAddresses: ?Array<string> } }
|
||||
action: { data: { coinSwaps: ?Array<CoinSwapInfo> } }
|
||||
) => {
|
||||
const { btcAddresses } = action.data;
|
||||
const sanitizedBtcAddresses = btcAddresses && btcAddresses.filter((e) => typeof e === 'string');
|
||||
const { coinSwaps } = action.data;
|
||||
const sanitizedCoinSwaps = coinSwaps && coinSwaps.filter((x) => typeof x.sendAddress === 'string');
|
||||
return {
|
||||
...state,
|
||||
btcAddresses:
|
||||
sanitizedBtcAddresses && sanitizedBtcAddresses.length ? sanitizedBtcAddresses : state.btcAddresses,
|
||||
coinSwaps: sanitizedCoinSwaps && sanitizedCoinSwaps.length ? sanitizedCoinSwaps : state.coinSwaps,
|
||||
};
|
||||
},
|
||||
},
|
||||
|
|
|
@ -3,6 +3,6 @@ import { createSelector } from 'reselect';
|
|||
|
||||
const selectState = (state: { coinSwap: CoinSwapState }) => state.coinSwap || {};
|
||||
|
||||
export const selectBtcAddresses = createSelector(selectState, (state: CoinSwapState) => {
|
||||
return state.btcAddresses.filter((x) => typeof x === 'string');
|
||||
export const selectCoinSwaps = createSelector(selectState, (state: CoinSwapState) => {
|
||||
return state.coinSwaps.filter((x) => typeof x.sendAddress === 'string');
|
||||
});
|
||||
|
|
10
ui/store.js
10
ui/store.js
|
@ -66,7 +66,7 @@ const searchFilter = createFilter('search', ['options']);
|
|||
const tagsFilter = createFilter('tags', ['followedTags']);
|
||||
const subscriptionsFilter = createFilter('subscriptions', ['subscriptions']);
|
||||
const blockedFilter = createFilter('blocked', ['blockedChannels']);
|
||||
const btcAddressesFilter = createFilter('coinSwap', ['btcAddresses']);
|
||||
const coinSwapsFilter = createFilter('coinSwap', ['coinSwaps']);
|
||||
const settingsFilter = createBlacklistFilter('settings', ['loadedLanguages', 'language']);
|
||||
const whiteListedReducers = [
|
||||
'fileInfo',
|
||||
|
@ -86,7 +86,7 @@ const transforms = [
|
|||
fileInfoFilter,
|
||||
walletFilter,
|
||||
blockedFilter,
|
||||
btcAddressesFilter,
|
||||
coinSwapsFilter,
|
||||
tagsFilter,
|
||||
appFilter,
|
||||
searchFilter,
|
||||
|
@ -122,8 +122,8 @@ const triggerSharedStateActions = [
|
|||
ACTIONS.CHANNEL_SUBSCRIBE,
|
||||
ACTIONS.CHANNEL_UNSUBSCRIBE,
|
||||
ACTIONS.TOGGLE_BLOCK_CHANNEL,
|
||||
ACTIONS.ADD_BTC_ADDRESS,
|
||||
ACTIONS.REMOVE_BTC_ADDRESS,
|
||||
ACTIONS.ADD_COIN_SWAP,
|
||||
ACTIONS.REMOVE_COIN_SWAP,
|
||||
ACTIONS.TOGGLE_TAG_FOLLOW,
|
||||
LBRY_REDUX_ACTIONS.CREATE_CHANNEL_COMPLETED,
|
||||
ACTIONS.SYNC_CLIENT_SETTINGS,
|
||||
|
@ -156,7 +156,7 @@ const sharedStateFilters = {
|
|||
property: 'following',
|
||||
},
|
||||
blocked: { source: 'blocked', property: 'blockedChannels' },
|
||||
btc_addresses: { source: 'coinSwap', property: 'btcAddresses' },
|
||||
coin_swaps: { source: 'coinSwap', property: 'coinSwaps' },
|
||||
settings: { source: 'settings', property: 'sharedPreferences' },
|
||||
app_welcome_version: { source: 'app', property: 'welcomeVersion' },
|
||||
sharing_3P: { source: 'app', property: 'allowAnalytics' },
|
||||
|
|
Loading…
Reference in a new issue